From af678802bb90fe8a0698141fe7a9635690b88650 Mon Sep 17 00:00:00 2001 From: veeso Date: Sat, 3 Apr 2021 16:21:37 +0200 Subject: [PATCH 1/3] Added path to HostError; scan_dir won't fail if it is not possible to stat an entry --- CHANGELOG.md | 1 + Cargo.lock | 21 ++++++++ Cargo.toml | 85 +++++++++++++++++------------- src/host/mod.rs | 135 +++++++++++++++++++++++++++--------------------- 4 files changed, 147 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcadfb8..4a62a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Released on FIXME: - [Issue 9](https://github.com/veeso/termscp/issues/9): Fixed issues related to paths on remote when using Windows - Dependencies: - Added `path-slash 0.1.4` (Windows only) + - Added `thiserror 1.0.24` ## 0.4.0 diff --git a/Cargo.lock b/Cargo.lock index 93090d0..f10cc4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1411,6 +1411,7 @@ dependencies = [ "ssh2", "tempfile", "textwrap", + "thiserror", "toml", "tui", "ureq", @@ -1429,6 +1430,26 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.1" diff --git a/Cargo.toml b/Cargo.toml index bda1615..7c73626 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,31 @@ [package] -name = "termscp" -version = "0.4.1" authors = ["Christian Visintin"] -edition = "2018" -license = "MIT" -keywords = ["scp-client", "sftp-client", "ftp-client", "winscp", "command-line-utility"] categories = ["command-line-utilities"] description = "TermSCP is a SCP/SFTP/FTPS client for command line with an integrated UI to explore the remote file system. Basically WinSCP on a terminal." -homepage = "https://github.com/veeso/termscp" -repository = "https://github.com/veeso/termscp" documentation = "https://docs.rs/termscp" -readme = "README.md" +edition = "2018" +homepage = "https://github.com/veeso/termscp" include = ["src/**/*", "LICENSE", "README.md", "CHANGELOG.md"] +keywords = ["scp-client", "sftp-client", "ftp-client", "winscp", "command-line-utility"] +license = "MIT" +name = "termscp" +readme = "README.md" +repository = "https://github.com/veeso/termscp" +version = "0.4.1" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[metadata] +[metadata.rpm] +package = "termscp" + +[metadata.rpm.cargo] +buildflags = ["--release"] + +[metadata.rpm.targets] +[metadata.rpm.targets.termscp] +path = "/usr/bin/termscp" +[[bin]] +name = "termscp" +path = "src/main.rs" [dependencies] bitflags = "1.2.1" @@ -23,7 +35,6 @@ content_inspector = "0.2.4" crossterm = "0.19.0" dirs = "3.0.1" edit = "0.1.2" -ftp4 = { version = "^4.0.2", features = ["secure"] } getopts = "0.2.21" hostname = "0.3.1" lazy_static = "1.4.0" @@ -31,41 +42,43 @@ magic-crypt = "3.1.6" rand = "0.8.2" regex = "1.4.2" rpassword = "5.0.1" -serde = { version = "1.0.121", features = ["derive"] } ssh2 = "0.9.0" tempfile = "3.1.0" textwrap = "0.13.1" +thiserror = "^1.0.0" toml = "0.5.8" -tui = { version = "0.14.0", features = ["crossterm"], default-features = false } -ureq = { version = "2.0.2", features = ["json"] } whoami = "1.1.0" wildmatch = "1.0.13" -# POSIX dependencies -[target.'cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))'.dependencies] +[dependencies.ftp4] +features = ["secure"] +version = "^4.0.2" + +[dependencies.serde] +features = ["derive"] +version = "1.0.121" + +[dependencies.tui] +default-features = false +features = ["crossterm"] +version = "0.14.0" + +[dependencies.ureq] +features = ["json"] +version = "2.0.2" + +[features] +githubActions = [] + +[target] +[target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))"] +[target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))".dependencies] users = "0.11.0" -# Windows + MacOS -[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies] +[target."cfg(any(target_os = \"windows\", target_os = \"macos\"))"] +[target."cfg(any(target_os = \"windows\", target_os = \"macos\"))".dependencies] keyring = "0.10.1" -# Windows dependencies -[target.'cfg(target_os = "windows")'.dependencies] +[target."cfg(target_os = \"windows\")"] +[target."cfg(target_os = \"windows\")".dependencies] path-slash = "0.1.4" - -# Features -[features] -githubActions = [] # used to run particular on github actions - -[[bin]] -name = "termscp" -path = "src/main.rs" - -[package.metadata.rpm] -package = "termscp" - -[package.metadata.rpm.cargo] -buildflags = ["--release"] - -[package.metadata.rpm.targets] -termscp = { path = "/usr/bin/termscp" } diff --git a/src/host/mod.rs b/src/host/mod.rs index d5ad17e..68abbbc 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -31,6 +31,7 @@ extern crate wildmatch; use std::fs::{self, File, Metadata, OpenOptions}; use std::path::{Path, PathBuf}; use std::time::SystemTime; +use thiserror::Error; use wildmatch::WildMatch; // Metadata ext #[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))] @@ -44,15 +45,23 @@ use crate::fs::{FsDirectory, FsEntry, FsFile}; /// ## HostErrorType /// /// HostErrorType provides an overview of the specific host error -#[derive(PartialEq, std::fmt::Debug)] +#[derive(Error, Debug)] pub enum HostErrorType { + #[error("No such file or directory")] NoSuchFileOrDirectory, + #[error("File is readonly")] ReadonlyFile, + #[error("Could not access directory")] DirNotAccessible, + #[error("Could not access file")] FileNotAccessible, + #[error("File already exists")] FileAlreadyExists, + #[error("Could not create file")] CouldNotCreateFile, + #[error("Command execution failed")] ExecutionFailed, + #[error("Could not delete file")] DeleteFailed, } @@ -62,36 +71,42 @@ pub enum HostErrorType { pub struct HostError { pub error: HostErrorType, - pub ioerr: Option, + ioerr: Option, + path: Option, } impl HostError { /// ### new /// /// Instantiates a new HostError - pub(crate) fn new(error: HostErrorType, errno: Option) -> HostError { + pub(crate) fn new(error: HostErrorType, errno: Option, p: &Path) -> Self { HostError { error, ioerr: errno, + path: Some(p.to_path_buf()) + } + } +} + +impl From for HostError { + fn from(error: HostErrorType) -> Self { + HostError { + error, + ioerr: None, + path: None, } } } impl std::fmt::Display for HostError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let code_str: &str = match self.error { - HostErrorType::NoSuchFileOrDirectory => "No such file or directory", - HostErrorType::ReadonlyFile => "File is readonly", - HostErrorType::DirNotAccessible => "Could not access directory", - HostErrorType::FileNotAccessible => "Could not access file", - HostErrorType::FileAlreadyExists => "File already exists", - HostErrorType::CouldNotCreateFile => "Could not create file", - HostErrorType::ExecutionFailed => "Could not run command", - HostErrorType::DeleteFailed => "Could not delete file", + let p_str: String = match self.path.as_ref() { + None => String::new(), + Some(p) => format!(" ({})", p.display().to_string()), }; match &self.ioerr { - Some(err) => write!(f, "{}: {}", code_str, err), - None => write!(f, "{}", code_str), + Some(err) => write!(f, "{}: {}{}", self.error, err, p_str), + None => write!(f, "{}{}", self.error, p_str), } } } @@ -116,7 +131,7 @@ impl Localhost { }; // Check if dir exists if !host.file_exists(host.wrkdir.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, host.wrkdir.as_path())); } // Retrieve files for provided path host.files = match host.scan_dir(host.wrkdir.as_path()) { @@ -148,11 +163,11 @@ impl Localhost { let new_dir: PathBuf = self.to_abs_path(new_dir); // Check whether directory exists if !self.file_exists(new_dir.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, new_dir.as_path())); } // Change directory if std::env::set_current_dir(new_dir.as_path()).is_err() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, new_dir.as_path())); } let prev_dir: PathBuf = self.wrkdir.clone(); // Backup location // Update working directory @@ -187,10 +202,10 @@ impl Localhost { if dir_path.exists() { match ignex { true => return Ok(()), - false => return Err(HostError::new(HostErrorType::FileAlreadyExists, None)), + false => return Err(HostError::new(HostErrorType::FileAlreadyExists, None, dir_path.as_path())), } } - match std::fs::create_dir(dir_path) { + match std::fs::create_dir(dir_path.as_path()) { Ok(_) => { // Update dir if dir_name.is_relative() { @@ -198,7 +213,7 @@ impl Localhost { } Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), dir_path.as_path())), } } @@ -210,7 +225,7 @@ impl Localhost { FsEntry::Directory(dir) => { // If file doesn't exist; return error if !dir.abs_path.as_path().exists() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, dir.abs_path.as_path())); } // Remove match std::fs::remove_dir_all(dir.abs_path.as_path()) { @@ -219,13 +234,13 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err), dir.abs_path.as_path())), } } FsEntry::File(file) => { // If file doesn't exist; return error if !file.abs_path.as_path().exists() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, file.abs_path.as_path())); } // Remove match std::fs::remove_file(file.abs_path.as_path()) { @@ -234,7 +249,7 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err), file.abs_path.as_path())), } } } @@ -251,7 +266,7 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), abs_path.as_path())), } } @@ -276,7 +291,7 @@ impl Localhost { }; // Copy entry path to dst path if let Err(err) = std::fs::copy(file.abs_path.as_path(), dst.as_path()) { - return Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err))); + return Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), file.abs_path.as_path())); } } FsEntry::Directory(dir) => { @@ -328,7 +343,7 @@ impl Localhost { let path: PathBuf = self.to_abs_path(path); let attr: Metadata = match fs::metadata(path.as_path()) { Ok(metadata) => metadata, - Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), }; let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); // Match dir / file @@ -389,7 +404,7 @@ impl Localhost { let path: PathBuf = self.to_abs_path(path); let attr: Metadata = match fs::metadata(path.as_path()) { Ok(metadata) => metadata, - Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), }; let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); // Match dir / file @@ -455,7 +470,7 @@ impl Localhost { Ok(s) => Ok(s.to_string()), Err(_) => Ok(String::new()), }, - Err(err) => Err(HostError::new(HostErrorType::ExecutionFailed, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::ExecutionFailed, Some(err), self.wrkdir.as_path())), } } @@ -472,10 +487,10 @@ impl Localhost { mpex.set_mode(self.mode_to_u32(pex)); match set_permissions(path.as_path(), mpex) { Ok(_) => Ok(()), - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), } } - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), } } @@ -485,7 +500,7 @@ impl Localhost { pub fn open_file_read(&self, file: &Path) -> Result { let file: PathBuf = self.to_abs_path(file); if !self.file_exists(file.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None)); + return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, file.as_path())); } match OpenOptions::new() .create(false) @@ -494,7 +509,7 @@ impl Localhost { .open(file.as_path()) { Ok(f) => Ok(f), - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), file.as_path())), } } @@ -511,8 +526,8 @@ impl Localhost { { Ok(f) => Ok(f), Err(err) => match self.file_exists(file.as_path()) { - true => Err(HostError::new(HostErrorType::ReadonlyFile, Some(err))), - false => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err))), + true => Err(HostError::new(HostErrorType::ReadonlyFile, Some(err), file.as_path())), + false => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), file.as_path())), }, } } @@ -526,20 +541,21 @@ impl Localhost { /// ### scan_dir /// - /// Get content of the current directory as a list of fs entry (Windows) + /// Get content of the current directory as a list of fs entry pub fn scan_dir(&self, dir: &Path) -> Result, HostError> { - let entries = match std::fs::read_dir(dir) { - Ok(e) => e, - Err(err) => return Err(HostError::new(HostErrorType::DirNotAccessible, Some(err))), - }; - let mut fs_entries: Vec = Vec::new(); - for entry in entries.flatten() { - fs_entries.push(match self.stat(entry.path().as_path()) { - Ok(entry) => entry, - Err(err) => return Err(err), - }); + match std::fs::read_dir(dir) { + Ok(e) => { + let mut fs_entries: Vec = Vec::new(); + for entry in e.flatten() { + // NOTE: 0.4.1, don't fail if stat for one file fails + if let Ok(entry) = self.stat(entry.path().as_path()) { + fs_entries.push(entry); + } + } + Ok(fs_entries) + }, + Err(err) => Err(HostError::new(HostErrorType::DirNotAccessible, Some(err), dir)), } - Ok(fs_entries) } /// ### find @@ -641,9 +657,9 @@ mod tests { #[test] fn test_host_error_new() { - let error: HostError = HostError::new(HostErrorType::CouldNotCreateFile, None); - assert_eq!(error.error, HostErrorType::CouldNotCreateFile); + let error: HostError = HostError::new(HostErrorType::CouldNotCreateFile, None, Path::new("/tmp")); assert!(error.ioerr.is_none()); + assert_eq!(error.path.as_ref().unwrap(), Path::new("/tmp")); } #[test] @@ -1068,40 +1084,41 @@ mod tests { let err: HostError = HostError::new( HostErrorType::CouldNotCreateFile, Some(std::io::Error::from(std::io::ErrorKind::AddrInUse)), + Path::new("/tmp"), ); assert_eq!( format!("{}", err), - String::from("Could not create file: address in use") + String::from("Could not create file: address in use (/tmp)"), ); assert_eq!( - format!("{}", HostError::new(HostErrorType::DeleteFailed, None)), + format!("{}", HostError::from(HostErrorType::DeleteFailed)), String::from("Could not delete file") ); assert_eq!( - format!("{}", HostError::new(HostErrorType::ExecutionFailed, None)), - String::from("Could not run command") + format!("{}", HostError::from(HostErrorType::ExecutionFailed)), + String::from("Command execution failed"), ); assert_eq!( - format!("{}", HostError::new(HostErrorType::DirNotAccessible, None)), - String::from("Could not access directory") + format!("{}", HostError::from(HostErrorType::DirNotAccessible)), + String::from("Could not access directory"), ); assert_eq!( format!( "{}", - HostError::new(HostErrorType::NoSuchFileOrDirectory, None) + HostError::from(HostErrorType::NoSuchFileOrDirectory) ), String::from("No such file or directory") ); assert_eq!( - format!("{}", HostError::new(HostErrorType::ReadonlyFile, None)), + format!("{}", HostError::from(HostErrorType::ReadonlyFile)), String::from("File is readonly") ); assert_eq!( - format!("{}", HostError::new(HostErrorType::FileNotAccessible, None)), + format!("{}", HostError::from(HostErrorType::FileNotAccessible)), String::from("Could not access file") ); assert_eq!( - format!("{}", HostError::new(HostErrorType::FileAlreadyExists, None)), + format!("{}", HostError::from(HostErrorType::FileAlreadyExists)), String::from("File already exists") ); } From 91081cb86a0e0696956e54b3220a757867ad043e Mon Sep 17 00:00:00 2001 From: veeso Date: Sat, 3 Apr 2021 16:33:18 +0200 Subject: [PATCH 2/3] Use thiserror to format error messages --- Cargo.toml | 3 +-- src/bookmarks/mod.rs | 17 +++++++---------- src/config/mod.rs | 17 +++++++---------- src/filetransfer/mod.rs | 35 ++++++++++++++++------------------- 4 files changed, 31 insertions(+), 41 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7c73626..8f30fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ readme = "README.md" repository = "https://github.com/veeso/termscp" version = "0.4.1" -[metadata] [metadata.rpm] package = "termscp" @@ -23,6 +22,7 @@ buildflags = ["--release"] [metadata.rpm.targets] [metadata.rpm.targets.termscp] path = "/usr/bin/termscp" + [[bin]] name = "termscp" path = "src/main.rs" @@ -70,7 +70,6 @@ version = "2.0.2" [features] githubActions = [] -[target] [target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))"] [target."cfg(any(target_os = \"unix\", target_os = \"macos\", target_os = \"linux\"))".dependencies] users = "0.11.0" diff --git a/src/bookmarks/mod.rs b/src/bookmarks/mod.rs index f593ee0..77c0c78 100644 --- a/src/bookmarks/mod.rs +++ b/src/bookmarks/mod.rs @@ -29,6 +29,7 @@ pub mod serializer; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use thiserror::Error; #[derive(Deserialize, Serialize, std::fmt::Debug)] /// ## UserHosts @@ -66,10 +67,13 @@ pub struct SerializerError { /// ## SerializerErrorKind /// /// Describes the kind of error for the serializer/deserializer -#[derive(std::fmt::Debug, PartialEq)] +#[derive(Error, Debug)] pub enum SerializerErrorKind { + #[error("IO error")] IoError, + #[error("Serialization error")] SerializationError, + #[error("Syntax error")] SyntaxError, } @@ -102,14 +106,9 @@ impl SerializerError { impl std::fmt::Display for SerializerError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let err: String = match &self.kind { - SerializerErrorKind::IoError => String::from("IO error"), - SerializerErrorKind::SerializationError => String::from("Serialization error"), - SerializerErrorKind::SyntaxError => String::from("Syntax error"), - }; match &self.msg { - Some(msg) => write!(f, "{} ({})", err, msg), - None => write!(f, "{}", err), + Some(msg) => write!(f, "{} ({})", self.kind, msg), + None => write!(f, "{}", self.kind), } } } @@ -172,12 +171,10 @@ mod tests { #[test] fn test_bookmarks_bookmark_errors() { let error: SerializerError = SerializerError::new(SerializerErrorKind::SyntaxError); - assert_eq!(error.kind, SerializerErrorKind::SyntaxError); assert!(error.msg.is_none()); assert_eq!(format!("{}", error), String::from("Syntax error")); let error: SerializerError = SerializerError::new_ex(SerializerErrorKind::SyntaxError, String::from("bad syntax")); - assert_eq!(error.kind, SerializerErrorKind::SyntaxError); assert!(error.msg.is_some()); assert_eq!( format!("{}", error), diff --git a/src/config/mod.rs b/src/config/mod.rs index 4c97aa1..f4fa64d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -38,6 +38,7 @@ use crate::filetransfer::FileTransferProtocol; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::PathBuf; +use thiserror::Error; #[derive(Deserialize, Serialize, std::fmt::Debug)] /// ## UserConfig @@ -117,10 +118,13 @@ pub struct SerializerError { /// ## SerializerErrorKind /// /// Describes the kind of error for the serializer/deserializer -#[derive(std::fmt::Debug, PartialEq)] +#[derive(Error, Debug)] pub enum SerializerErrorKind { + #[error("IO error")] IoError, + #[error("Serialization error")] SerializationError, + #[error("Syntax error")] SyntaxError, } @@ -144,14 +148,9 @@ impl SerializerError { impl std::fmt::Display for SerializerError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let err: String = match &self.kind { - SerializerErrorKind::IoError => String::from("IO error"), - SerializerErrorKind::SerializationError => String::from("Serialization error"), - SerializerErrorKind::SyntaxError => String::from("Syntax error"), - }; match &self.msg { - Some(msg) => write!(f, "{} ({})", err, msg), - None => write!(f, "{}", err), + Some(msg) => write!(f, "{} ({})", self.kind, msg), + None => write!(f, "{}", self.kind), } } } @@ -214,12 +213,10 @@ mod tests { #[test] fn test_config_mod_errors() { let error: SerializerError = SerializerError::new(SerializerErrorKind::SyntaxError); - assert_eq!(error.kind, SerializerErrorKind::SyntaxError); assert!(error.msg.is_none()); assert_eq!(format!("{}", error), String::from("Syntax error")); let error: SerializerError = SerializerError::new_ex(SerializerErrorKind::SyntaxError, String::from("bad syntax")); - assert_eq!(error.kind, SerializerErrorKind::SyntaxError); assert!(error.msg.is_some()); assert_eq!( format!("{}", error), diff --git a/src/filetransfer/mod.rs b/src/filetransfer/mod.rs index 3d6525d..b07cb76 100644 --- a/src/filetransfer/mod.rs +++ b/src/filetransfer/mod.rs @@ -32,6 +32,7 @@ use crate::fs::{FsEntry, FsFile}; // ext use std::io::{Read, Write}; use std::path::{Path, PathBuf}; +use thiserror::Error; use wildmatch::WildMatch; // exports pub mod ftp_transfer; @@ -62,19 +63,31 @@ pub struct FileTransferError { /// /// FileTransferErrorType defines the possible errors available for a file transfer #[allow(dead_code)] -#[derive(std::fmt::Debug)] +#[derive(Error, Debug)] pub enum FileTransferErrorType { + #[error("Authentication failed")] AuthenticationFailed, + #[error("Bad address syntax")] BadAddress, + #[error("Connection error")] ConnectionError, + #[error("SSL error")] SslError, + #[error("Could not stat directory")] DirStatFailed, + #[error("Failed to create file")] FileCreateDenied, + #[error("IO error: {0}")] IoErr(std::io::Error), + #[error("No such file or directory")] NoSuchFileOrDirectory, + #[error("Not enough permissions")] PexError, + #[error("Protocol error")] ProtocolError, + #[error("Uninitialized session")] UninitializedSession, + #[error("Unsupported feature")] UnsupportedFeature, } @@ -98,25 +111,9 @@ impl FileTransferError { impl std::fmt::Display for FileTransferError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - let err: String = match &self.code { - FileTransferErrorType::AuthenticationFailed => String::from("Authentication failed"), - FileTransferErrorType::BadAddress => String::from("Bad address syntax"), - FileTransferErrorType::ConnectionError => String::from("Connection error"), - FileTransferErrorType::DirStatFailed => String::from("Could not stat directory"), - FileTransferErrorType::FileCreateDenied => String::from("Failed to create file"), - FileTransferErrorType::IoErr(err) => format!("IO error: {}", err), - FileTransferErrorType::NoSuchFileOrDirectory => { - String::from("No such file or directory") - } - FileTransferErrorType::PexError => String::from("Not enough permissions"), - FileTransferErrorType::ProtocolError => String::from("Protocol error"), - FileTransferErrorType::SslError => String::from("SSL error"), - FileTransferErrorType::UninitializedSession => String::from("Uninitialized session"), - FileTransferErrorType::UnsupportedFeature => String::from("Unsupported feature"), - }; match &self.msg { - Some(msg) => write!(f, "{} ({})", err, msg), - None => write!(f, "{}", err), + Some(msg) => write!(f, "{} ({})", self.code, msg), + None => write!(f, "{}", self.code), } } } From c0ae9222640a49dea075f072dc4585579926c155 Mon Sep 17 00:00:00 2001 From: veeso Date: Sat, 3 Apr 2021 16:48:37 +0200 Subject: [PATCH 3/3] format --- src/host/mod.rs | 144 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 28 deletions(-) diff --git a/src/host/mod.rs b/src/host/mod.rs index 68abbbc..f1d985c 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -83,7 +83,7 @@ impl HostError { HostError { error, ioerr: errno, - path: Some(p.to_path_buf()) + path: Some(p.to_path_buf()), } } } @@ -131,7 +131,11 @@ impl Localhost { }; // Check if dir exists if !host.file_exists(host.wrkdir.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, host.wrkdir.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + host.wrkdir.as_path(), + )); } // Retrieve files for provided path host.files = match host.scan_dir(host.wrkdir.as_path()) { @@ -163,11 +167,19 @@ impl Localhost { let new_dir: PathBuf = self.to_abs_path(new_dir); // Check whether directory exists if !self.file_exists(new_dir.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, new_dir.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + new_dir.as_path(), + )); } // Change directory if std::env::set_current_dir(new_dir.as_path()).is_err() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, new_dir.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + new_dir.as_path(), + )); } let prev_dir: PathBuf = self.wrkdir.clone(); // Backup location // Update working directory @@ -202,7 +214,13 @@ impl Localhost { if dir_path.exists() { match ignex { true => return Ok(()), - false => return Err(HostError::new(HostErrorType::FileAlreadyExists, None, dir_path.as_path())), + false => { + return Err(HostError::new( + HostErrorType::FileAlreadyExists, + None, + dir_path.as_path(), + )) + } } } match std::fs::create_dir(dir_path.as_path()) { @@ -213,7 +231,11 @@ impl Localhost { } Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), dir_path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::CouldNotCreateFile, + Some(err), + dir_path.as_path(), + )), } } @@ -225,7 +247,11 @@ impl Localhost { FsEntry::Directory(dir) => { // If file doesn't exist; return error if !dir.abs_path.as_path().exists() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, dir.abs_path.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + dir.abs_path.as_path(), + )); } // Remove match std::fs::remove_dir_all(dir.abs_path.as_path()) { @@ -234,13 +260,21 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err), dir.abs_path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::DeleteFailed, + Some(err), + dir.abs_path.as_path(), + )), } } FsEntry::File(file) => { // If file doesn't exist; return error if !file.abs_path.as_path().exists() { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, file.abs_path.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + file.abs_path.as_path(), + )); } // Remove match std::fs::remove_file(file.abs_path.as_path()) { @@ -249,7 +283,11 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::DeleteFailed, Some(err), file.abs_path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::DeleteFailed, + Some(err), + file.abs_path.as_path(), + )), } } } @@ -266,7 +304,11 @@ impl Localhost { self.files = self.scan_dir(self.wrkdir.as_path())?; Ok(()) } - Err(err) => Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), abs_path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::CouldNotCreateFile, + Some(err), + abs_path.as_path(), + )), } } @@ -291,7 +333,11 @@ impl Localhost { }; // Copy entry path to dst path if let Err(err) = std::fs::copy(file.abs_path.as_path(), dst.as_path()) { - return Err(HostError::new(HostErrorType::CouldNotCreateFile, Some(err), file.abs_path.as_path())); + return Err(HostError::new( + HostErrorType::CouldNotCreateFile, + Some(err), + file.abs_path.as_path(), + )); } } FsEntry::Directory(dir) => { @@ -343,7 +389,13 @@ impl Localhost { let path: PathBuf = self.to_abs_path(path); let attr: Metadata = match fs::metadata(path.as_path()) { Ok(metadata) => metadata, - Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), + Err(err) => { + return Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + path.as_path(), + )) + } }; let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); // Match dir / file @@ -404,7 +456,13 @@ impl Localhost { let path: PathBuf = self.to_abs_path(path); let attr: Metadata = match fs::metadata(path.as_path()) { Ok(metadata) => metadata, - Err(err) => return Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), + Err(err) => { + return Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + path.as_path(), + )) + } }; let file_name: String = String::from(path.file_name().unwrap().to_str().unwrap_or("")); // Match dir / file @@ -470,7 +528,11 @@ impl Localhost { Ok(s) => Ok(s.to_string()), Err(_) => Ok(String::new()), }, - Err(err) => Err(HostError::new(HostErrorType::ExecutionFailed, Some(err), self.wrkdir.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::ExecutionFailed, + Some(err), + self.wrkdir.as_path(), + )), } } @@ -487,10 +549,18 @@ impl Localhost { mpex.set_mode(self.mode_to_u32(pex)); match set_permissions(path.as_path(), mpex) { Ok(_) => Ok(()), - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + path.as_path(), + )), } } - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), path.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + path.as_path(), + )), } } @@ -500,7 +570,11 @@ impl Localhost { pub fn open_file_read(&self, file: &Path) -> Result { let file: PathBuf = self.to_abs_path(file); if !self.file_exists(file.as_path()) { - return Err(HostError::new(HostErrorType::NoSuchFileOrDirectory, None, file.as_path())); + return Err(HostError::new( + HostErrorType::NoSuchFileOrDirectory, + None, + file.as_path(), + )); } match OpenOptions::new() .create(false) @@ -509,7 +583,11 @@ impl Localhost { .open(file.as_path()) { Ok(f) => Ok(f), - Err(err) => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), file.as_path())), + Err(err) => Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + file.as_path(), + )), } } @@ -526,8 +604,16 @@ impl Localhost { { Ok(f) => Ok(f), Err(err) => match self.file_exists(file.as_path()) { - true => Err(HostError::new(HostErrorType::ReadonlyFile, Some(err), file.as_path())), - false => Err(HostError::new(HostErrorType::FileNotAccessible, Some(err), file.as_path())), + true => Err(HostError::new( + HostErrorType::ReadonlyFile, + Some(err), + file.as_path(), + )), + false => Err(HostError::new( + HostErrorType::FileNotAccessible, + Some(err), + file.as_path(), + )), }, } } @@ -553,8 +639,12 @@ impl Localhost { } } Ok(fs_entries) - }, - Err(err) => Err(HostError::new(HostErrorType::DirNotAccessible, Some(err), dir)), + } + Err(err) => Err(HostError::new( + HostErrorType::DirNotAccessible, + Some(err), + dir, + )), } } @@ -657,7 +747,8 @@ mod tests { #[test] fn test_host_error_new() { - let error: HostError = HostError::new(HostErrorType::CouldNotCreateFile, None, Path::new("/tmp")); + let error: HostError = + HostError::new(HostErrorType::CouldNotCreateFile, None, Path::new("/tmp")); assert!(error.ioerr.is_none()); assert_eq!(error.path.as_ref().unwrap(), Path::new("/tmp")); } @@ -1103,10 +1194,7 @@ mod tests { String::from("Could not access directory"), ); assert_eq!( - format!( - "{}", - HostError::from(HostErrorType::NoSuchFileOrDirectory) - ), + format!("{}", HostError::from(HostErrorType::NoSuchFileOrDirectory)), String::from("No such file or directory") ); assert_eq!(