mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-06-02 07:20:48 +00:00
Check if the sort field is supported by the OS
This commit is contained in:
parent
56717c7336
commit
39a49a3d36
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
"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 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)
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user