ソースを参照

Check if the sort field is supported by the OS

ariasuni 7 年 前
コミット
39a49a3d36
5 ファイル変更110 行追加62 行削除
  1. 37 1
      src/fs/file.rs
  2. 1 1
      src/fs/mod.rs
  3. 42 48
      src/options/filter.rs
  4. 4 0
      src/options/misfire.rs
  5. 26 12
      src/options/view.rs

+ 37 - 1
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 {

+ 1 - 1
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;

+ 42 - 48
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

+ 4 - 0
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),

+ 26 - 12
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)
     }
 }