diff options
author | ariasuni <perso@hack-libre.org> | 2018-12-17 05:48:49 +0100 |
---|---|---|
committer | ariasuni <perso@hack-libre.org> | 2019-03-23 17:23:43 +0100 |
commit | 39a49a3d3691f955756da73502b54ad48fc85c4e (patch) | |
tree | 7bb1a8aef3817a0bd1e61dcd72b12097e131c308 | |
parent | 56717c73366f2a78cf9bbff3a64c609551b26901 (diff) |
Check if the sort field is supported by the OS
-rw-r--r-- | src/fs/file.rs | 38 | ||||
-rw-r--r-- | src/fs/mod.rs | 2 | ||||
-rw-r--r-- | src/options/filter.rs | 90 | ||||
-rw-r--r-- | src/options/misfire.rs | 4 | ||||
-rw-r--r-- | src/options/view.rs | 38 |
5 files changed, 110 insertions, 62 deletions
diff --git a/src/fs/file.rs b/src/fs/file.rs index b73398b..22b51ce 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,6 +1,6 @@ //! Files, and methods and fields to access their metadata. -use std::fs; +use std::fs::{self, metadata}; use std::io::Error as IOError; use std::io::Result as IOResult; use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt}; @@ -9,6 +9,7 @@ use std::time::{UNIX_EPOCH, Duration}; use fs::dir::Dir; use fs::fields as f; +use options::Misfire; /// A **File** is a wrapper around one of Rust's Path objects, along with @@ -430,6 +431,41 @@ impl<'dir> FileTarget<'dir> { } +pub enum PlatformMetadata { + ModifiedTime, + ChangedTime, + AccessedTime, + CreatedTime, +} + +impl PlatformMetadata { + pub fn check_supported(&self) -> Result<(), Misfire> { + use std::env::temp_dir; + let result = match self { + // Call the functions that return a Result to see if it works + PlatformMetadata::AccessedTime => metadata(temp_dir()).unwrap().accessed(), + PlatformMetadata::ModifiedTime => metadata(temp_dir()).unwrap().modified(), + PlatformMetadata::CreatedTime => metadata(temp_dir()).unwrap().created(), + // We use the Unix API so we know it’s not available elsewhere + PlatformMetadata::ChangedTime => { + if cfg!(target_family = "unix") { + return Ok(()) + } else { + return Err(Misfire::Unsupported( + // for consistency, this error message similar to the one Rust + // use when created time is not available + "status modified time is not available on this platform currently".to_string())); + } + }, + }; + match result { + Ok(_) => Ok(()), + Err(err) => Err(Misfire::Unsupported(err.to_string())) + } + } +} + + /// More readable aliases for the permission bits exposed by libc. #[allow(trivial_numeric_casts)] mod modes { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 3275ccf..f6ca48e 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,7 +2,7 @@ mod dir; pub use self::dir::{Dir, DotFilter}; mod file; -pub use self::file::{File, FileTarget}; +pub use self::file::{File, FileTarget, PlatformMetadata}; pub mod feature; pub mod fields; diff --git a/src/options/filter.rs b/src/options/filter.rs index c56eba4..27c76cf 100644 --- a/src/options/filter.rs +++ b/src/options/filter.rs @@ -1,6 +1,6 @@ //! Parsing the options for `FileFilter`. -use fs::DotFilter; +use fs::{DotFilter, PlatformMetadata}; use fs::filter::{FileFilter, SortField, SortCase, IgnorePatterns, GitIgnore}; use options::{flags, Misfire}; @@ -35,65 +35,59 @@ impl SortField { None => return Ok(SortField::default()), }; - // The field is an OsStr, so can’t be matched. - if word == "name" || word == "filename" { - Ok(SortField::Name(SortCase::AaBbCc)) - } - else if word == "Name" || word == "Filename" { - Ok(SortField::Name(SortCase::ABCabc)) - } - else if word == ".name" || word == ".filename" { - Ok(SortField::NameMixHidden(SortCase::AaBbCc)) - } - else if word == ".Name" || word == ".Filename" { - Ok(SortField::NameMixHidden(SortCase::ABCabc)) - } - else if word == "size" || word == "filesize" { - Ok(SortField::Size) - } - else if word == "ext" || word == "extension" { - Ok(SortField::Extension(SortCase::AaBbCc)) - } - else if word == "Ext" || word == "Extension" { - Ok(SortField::Extension(SortCase::ABCabc)) - } - else if word == "date" || word == "time" || word == "mod" || word == "modified" || word == "new" || word == "newest" { + // Get String because we can’t match an OsStr + let word = match word.to_str() { + Some(ref w) => *w, + None => return Err(Misfire::BadArgument(&flags::SORT, word.into())) + }; + + let field = match word { + "name" | "filename" => SortField::Name(SortCase::AaBbCc), + "Name" | "Filename" => SortField::Name(SortCase::ABCabc), + ".name" | ".filename" => SortField::NameMixHidden(SortCase::AaBbCc), + ".Name" | ".Filename" => SortField::NameMixHidden(SortCase::ABCabc), + "size" | "filesize" => SortField::Size, + "ext" | "extension" => SortField::Extension(SortCase::AaBbCc), + "Ext" | "Extension" => SortField::Extension(SortCase::ABCabc), // “new” sorts oldest at the top and newest at the bottom; “old” // sorts newest at the top and oldest at the bottom. I think this // is the right way round to do this: “size” puts the smallest at // the top and the largest at the bottom, doesn’t it? - Ok(SortField::ModifiedDate) - } - else if word == "age" || word == "old" || word == "oldest" { + "date" | "time" | "mod" | "modified" | "new" | "newest" => SortField::ModifiedDate, // Similarly, “age” means that files with the least age (the // newest files) get sorted at the top, and files with the most // age (the oldest) at the bottom. - Ok(SortField::ModifiedAge) - } - else if word == "ch" || word == "changed" { - Ok(SortField::ChangedDate) - } - else if word == "acc" || word == "accessed" { - Ok(SortField::AccessedDate) - } - else if word == "cr" || word == "created" { - Ok(SortField::CreatedDate) - } - else if word == "inode" { - Ok(SortField::FileInode) - } - else if word == "type" { - Ok(SortField::FileType) - } - else if word == "none" { - Ok(SortField::Unsorted) + "age" | "old" | "oldest" => SortField::ModifiedAge, + "ch" | "changed" => SortField::ChangedDate, + "acc" | "accessed" => SortField::AccessedDate, + "cr" | "created" => SortField::CreatedDate, + "inode" => SortField::FileInode, + "type" => SortField::FileType, + "none" => SortField::Unsorted, + _ => return Err(Misfire::BadArgument(&flags::SORT, word.into())) + }; + + match SortField::to_platform_metadata(field) { + Some(m) => match m.check_supported() { + Ok(_) => Ok(field), + Err(misfire) => Err(misfire), + }, + None => Ok(field), } - else { - Err(Misfire::BadArgument(&flags::SORT, word.into())) + } + + fn to_platform_metadata(field: Self) -> Option<PlatformMetadata> { + match field { + SortField::ModifiedDate => Some(PlatformMetadata::ModifiedTime), + SortField::ChangedDate => Some(PlatformMetadata::ChangedTime), + SortField::AccessedDate => Some(PlatformMetadata::AccessedTime), + SortField::CreatedDate => Some(PlatformMetadata::CreatedTime), + _ => None } } } + // I’ve gone back and forth between whether to sort case-sensitively or // insensitively by default. The default string sort in most programming // languages takes each character’s ASCII value into account, sorting diff --git a/src/options/misfire.rs b/src/options/misfire.rs index da9174a..362bb2d 100644 --- a/src/options/misfire.rs +++ b/src/options/misfire.rs @@ -19,6 +19,9 @@ pub enum Misfire { /// The user supplied an illegal choice to an Argument. BadArgument(&'static Arg, OsString), + /// The user supplied a set of options + Unsupported(String), + /// The user asked for help. This isn’t strictly an error, which is why /// this enum isn’t named Error! Help(HelpString), @@ -83,6 +86,7 @@ impl fmt::Display for Misfire { } }, InvalidOptions(ref e) => write!(f, "{}", e), + Unsupported(ref e) => write!(f, "{}", e), Help(ref text) => write!(f, "{}", text), Version(ref version) => write!(f, "{}", version), Conflict(ref a, ref b) => write!(f, "Option {} conflicts with option {}", a, b), diff --git a/src/options/view.rs b/src/options/view.rs index d9a28b6..2420eb1 100644 --- a/src/options/view.rs +++ b/src/options/view.rs @@ -6,6 +6,7 @@ use output::time::TimeFormat; use options::{flags, Misfire, Vars}; use options::parser::MatchedFlags; +use fs::PlatformMetadata; use fs::feature::xattr; @@ -300,41 +301,54 @@ impl TimeTypes { let accessed = matches.has(&flags::ACCESSED)?; let created = matches.has(&flags::CREATED)?; - if let Some(word) = possible_word { + let time_types = if let Some(word) = possible_word { if modified { - Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME)) + return Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME)); } else if changed { - Err(Misfire::Useless(&flags::CHANGED, true, &flags::TIME)) + return Err(Misfire::Useless(&flags::CHANGED, true, &flags::TIME)); } else if accessed { - Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME)) + return Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME)); } else if created { - Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME)) + return Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME)); } else if word == "mod" || word == "modified" { - Ok(TimeTypes { modified: true, changed: false, accessed: false, created: false }) + TimeTypes { modified: true, changed: false, accessed: false, created: false } } else if word == "ch" || word == "changed" { - Ok(TimeTypes { modified: false, changed: true, accessed: false, created: false }) + TimeTypes { modified: false, changed: true, accessed: false, created: false } } else if word == "acc" || word == "accessed" { - Ok(TimeTypes { modified: false, changed: false, accessed: true, created: false }) + TimeTypes { modified: false, changed: false, accessed: true, created: false } } else if word == "cr" || word == "created" { - Ok(TimeTypes { modified: false, changed: false, accessed: false, created: true }) + TimeTypes { modified: false, changed: false, accessed: false, created: true } } else { - Err(Misfire::BadArgument(&flags::TIME, word.into())) + return Err(Misfire::BadArgument(&flags::TIME, word.into())); } } else if modified || changed || accessed || created { - Ok(TimeTypes { modified, changed, accessed, created }) + TimeTypes { modified, changed, accessed, created } } else { - Ok(TimeTypes::default()) + TimeTypes::default() + }; + + let mut fields = vec![]; + if time_types.modified { fields.push(PlatformMetadata::ModifiedTime); } + if time_types.changed { fields.push(PlatformMetadata::ChangedTime); } + if time_types.accessed { fields.push(PlatformMetadata::AccessedTime); } + if time_types.created { fields.push(PlatformMetadata::CreatedTime); } + + for field in fields { + if let Err(misfire) = field.check_supported() { + return Err(misfire); + } } + Ok(time_types) } } |