summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorariasuni <perso@hack-libre.org>2018-12-17 05:48:49 +0100
committerariasuni <perso@hack-libre.org>2019-03-23 17:23:43 +0100
commit39a49a3d3691f955756da73502b54ad48fc85c4e (patch)
tree7bb1a8aef3817a0bd1e61dcd72b12097e131c308
parent56717c73366f2a78cf9bbff3a64c609551b26901 (diff)
Check if the sort field is supported by the OS
-rw-r--r--src/fs/file.rs38
-rw-r--r--src/fs/mod.rs2
-rw-r--r--src/options/filter.rs90
-rw-r--r--src/options/misfire.rs4
-rw-r--r--src/options/view.rs38
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)
}
}