From 2ffa64cff6f00666d1ff47c146bff6938dfdcf5c Mon Sep 17 00:00:00 2001 From: Ben S Date: Thu, 26 Mar 2015 00:37:12 +0000 Subject: [PATCH] Move all optional features into features module This module provides feature-specific implementations, and also dummy implementations for when they aren't supported by the system or OS. Doing it this way limits all the #[cfg(feature)] annotations, as we can now just include the module or not. --- src/dir.rs | 111 ++----------------------- src/feature/git.rs | 85 +++++++++++++++++++ src/feature/mod.rs | 62 ++++++++++++++ src/{xattr => feature}/xattr_darwin.rs | 31 +++---- src/{xattr => feature}/xattr_linux.rs | 30 +++---- src/file.rs | 9 +- src/main.rs | 2 +- src/options.rs | 12 +-- src/output/details.rs | 2 +- src/xattr/mod.rs | 13 --- src/xattr/xattr_other.rs | 32 ------- 11 files changed, 196 insertions(+), 193 deletions(-) create mode 100644 src/feature/git.rs create mode 100644 src/feature/mod.rs rename src/{xattr => feature}/xattr_darwin.rs (85%) rename src/{xattr => feature}/xattr_linux.rs (83%) delete mode 100644 src/xattr/mod.rs delete mode 100644 src/xattr/xattr_other.rs diff --git a/src/dir.rs b/src/dir.rs index 9799ab3..623ff3e 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -1,11 +1,9 @@ use std::old_io::{fs, IoResult}; use std::old_path::GenericPath; use std::old_path::posix::Path; -use file::{File, GREY}; -#[cfg(feature="git")] use ansi_term::{ANSIString, ANSIStrings}; -#[cfg(feature="git")] use ansi_term::Colour::*; -#[cfg(feature="git")] use git2; +use feature::Git; +use file::{File, GREY}; /// A **Dir** provides a cached list of the file paths in a directory that's /// being listed. @@ -20,6 +18,7 @@ pub struct Dir { } impl Dir { + /// Create a new Dir object filled with all the files in the directory /// pointed to by the given path. Fails if the directory can't be read, or /// isn't actually a directory. @@ -67,107 +66,9 @@ impl Dir { /// Get a string describing the Git status of the given file. pub fn git_status(&self, path: &Path, prefix_lookup: bool) -> String { match (&self.git, prefix_lookup) { - (&Some(ref git), false) => git.status(path), - (&Some(ref git), true) => git.dir_status(path), - (&None, _) => GREY.paint("--").to_string(), + (&Some(ref git), false) => git.status(path), + (&Some(ref git), true) => git.dir_status(path), + (&None, _) => GREY.paint("--").to_string(), } } } - -/// Container of Git statuses for all the files in this folder's Git repository. -#[cfg(feature="git")] -struct Git { - statuses: Vec<(Path, git2::Status)>, -} - -#[cfg(feature="git")] -impl Git { - - /// Discover a Git repository on or above this directory, scanning it for - /// the files' statuses if one is found. - fn scan(path: &Path) -> Result { - use std::os::unix::ffi::OsStrExt; - use std::ffi::AsOsStr; - - // TODO: libgit2-rs uses the new Path module, but exa still uses the - // old_path one, and will have to continue to do so until the new IO - // module gets a bit more developed. So we have to turn Paths into - // old_path::Paths. Yes, this is hacky, but hopefully temporary. - let repo = try!(git2::Repository::discover(path)); - let workdir = match repo.workdir() { - Some(w) => Path::new(w.as_os_str().as_bytes()), - None => return Ok(Git { statuses: vec![] }), // bare repo - }; - - let statuses = try!(repo.statuses(None)).iter() - .map(|e| (workdir.join(e.path_bytes()), e.status())) - .collect(); - Ok(Git { statuses: statuses }) - } - - /// Get the status for the file at the given path, if present. - fn status(&self, path: &Path) -> String { - let status = self.statuses.iter() - .find(|p| &p.0 == path); - match status { - Some(&(_, s)) => ANSIStrings( &[Git::index_status(s), Git::working_tree_status(s) ]).to_string(), - None => GREY.paint("--").to_string(), - } - } - - /// Get the combined status for all the files whose paths begin with the - /// path that gets passed in. This is used for getting the status of - /// directories, which don't really have an 'official' status. - fn dir_status(&self, dir: &Path) -> String { - let s = self.statuses.iter() - .filter(|p| dir.is_ancestor_of(&p.0)) - .fold(git2::Status::empty(), |a, b| a | b.1); - - ANSIStrings( &[Git::index_status(s), Git::working_tree_status(s)] ).to_string() - } - - /// The character to display if the file has been modified, but not staged. - fn working_tree_status(status: git2::Status) -> ANSIString<'static> { - match status { - s if s.contains(git2::STATUS_WT_NEW) => Green.paint("A"), - s if s.contains(git2::STATUS_WT_MODIFIED) => Blue.paint("M"), - s if s.contains(git2::STATUS_WT_DELETED) => Red.paint("D"), - s if s.contains(git2::STATUS_WT_RENAMED) => Yellow.paint("R"), - s if s.contains(git2::STATUS_WT_TYPECHANGE) => Purple.paint("T"), - _ => GREY.paint("-"), - } - } - - /// The character to display if the file has been modified, and the change - /// has been staged. - fn index_status(status: git2::Status) -> ANSIString<'static> { - match status { - s if s.contains(git2::STATUS_INDEX_NEW) => Green.paint("A"), - s if s.contains(git2::STATUS_INDEX_MODIFIED) => Blue.paint("M"), - s if s.contains(git2::STATUS_INDEX_DELETED) => Red.paint("D"), - s if s.contains(git2::STATUS_INDEX_RENAMED) => Yellow.paint("R"), - s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => Purple.paint("T"), - _ => GREY.paint("-"), - } - } -} - -#[cfg(not(feature="git"))] -struct Git; - -#[cfg(not(feature="git"))] -impl Git { - fn scan(_: &Path) -> Result { - // Don't do anything without Git support - Err(()) - } - - fn status(&self, _: &Path) -> String { - // The Err above means that this should never happen - panic!("Tried to access a Git repo without Git support!"); - } - - fn dir_status(&self, path: &Path) -> String { - self.status(path) - } -} diff --git a/src/feature/git.rs b/src/feature/git.rs new file mode 100644 index 0000000..cc8a033 --- /dev/null +++ b/src/feature/git.rs @@ -0,0 +1,85 @@ +use std::old_path::GenericPath; +use std::old_path::posix::Path; + +use ansi_term::{ANSIString, ANSIStrings}; +use ansi_term::Colour::*; +use git2; + +use file::GREY; + +/// Container of Git statuses for all the files in this folder's Git repository. +pub struct Git { + statuses: Vec<(Path, git2::Status)>, +} + +impl Git { + + /// Discover a Git repository on or above this directory, scanning it for + /// the files' statuses if one is found. + pub fn scan(path: &Path) -> Result { + use std::os::unix::ffi::OsStrExt; + use std::ffi::AsOsStr; + + // TODO: libgit2-rs uses the new Path module, but exa still uses the + // old_path one, and will have to continue to do so until the new IO + // module gets a bit more developed. So we have to turn Paths into + // old_path::Paths. Yes, this is hacky, but hopefully temporary. + let new_path = path.as_os_str(); + let repo = try!(git2::Repository::discover(new_path)); + let workdir = match repo.workdir() { + Some(w) => Path::new(w.as_os_str().as_bytes()), + None => return Ok(Git { statuses: vec![] }), // bare repo + }; + + let statuses = try!(repo.statuses(None)).iter() + .map(|e| (workdir.join(e.path_bytes()), e.status())) + .collect(); + Ok(Git { statuses: statuses }) + } + + /// Get the status for the file at the given path, if present. + pub fn status(&self, path: &Path) -> String { + let status = self.statuses.iter() + .find(|p| &p.0 == path); + match status { + Some(&(_, s)) => ANSIStrings( &[Git::index_status(s), Git::working_tree_status(s) ]).to_string(), + None => GREY.paint("--").to_string(), + } + } + + /// Get the combined status for all the files whose paths begin with the + /// path that gets passed in. This is used for getting the status of + /// directories, which don't really have an 'official' status. + pub fn dir_status(&self, dir: &Path) -> String { + let s = self.statuses.iter() + .filter(|p| dir.is_ancestor_of(&p.0)) + .fold(git2::Status::empty(), |a, b| a | b.1); + + ANSIStrings( &[Git::index_status(s), Git::working_tree_status(s)] ).to_string() + } + + /// The character to display if the file has been modified, but not staged. + fn working_tree_status(status: git2::Status) -> ANSIString<'static> { + match status { + s if s.contains(git2::STATUS_WT_NEW) => Green.paint("A"), + s if s.contains(git2::STATUS_WT_MODIFIED) => Blue.paint("M"), + s if s.contains(git2::STATUS_WT_DELETED) => Red.paint("D"), + s if s.contains(git2::STATUS_WT_RENAMED) => Yellow.paint("R"), + s if s.contains(git2::STATUS_WT_TYPECHANGE) => Purple.paint("T"), + _ => GREY.paint("-"), + } + } + + /// The character to display if the file has been modified, and the change + /// has been staged. + fn index_status(status: git2::Status) -> ANSIString<'static> { + match status { + s if s.contains(git2::STATUS_INDEX_NEW) => Green.paint("A"), + s if s.contains(git2::STATUS_INDEX_MODIFIED) => Blue.paint("M"), + s if s.contains(git2::STATUS_INDEX_DELETED) => Red.paint("D"), + s if s.contains(git2::STATUS_INDEX_RENAMED) => Yellow.paint("R"), + s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => Purple.paint("T"), + _ => GREY.paint("-"), + } + } +} diff --git a/src/feature/mod.rs b/src/feature/mod.rs new file mode 100644 index 0000000..8fa0491 --- /dev/null +++ b/src/feature/mod.rs @@ -0,0 +1,62 @@ +// Extended attribute support + +#[cfg(target_os = "macos")] mod xattr_darwin; +#[cfg(target_os = "macos")] pub use self::xattr_darwin::Attribute; + +#[cfg(target_os = "linux")] mod xattr_linux; +#[cfg(target_os = "linux")] pub use self::xattr_linux::Attribute; + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] use std::old_io as io; +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +#[derive(Clone)] +pub struct Attribute; + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +impl Attribute { + + /// Getter for name + pub fn name(&self) -> &str { + unimplemented!() + } + + /// Getter for size + pub fn size(&self) -> usize { + unimplemented!() + } + + /// Lists the extended attributes. Follows symlinks like `stat` + pub fn list(_: &Path) -> io::IoResult> { + Ok(Vec::new()) + } + + /// Lists the extended attributes. Does not follow symlinks like `lstat` + pub fn llist(_: &Path) -> io::IoResult> { + Ok(Vec::new()) + } + + pub fn feature_implemented() -> bool { false } +} + + + +// Git support + +#[cfg(feature="git")] mod git; +#[cfg(feature="git")] pub use self::git::Git; + +#[cfg(not(feature="git"))] pub struct Git; +#[cfg(not(feature="git"))] use std::old_path::posix::Path; +#[cfg(not(feature="git"))] +impl Git { + pub fn scan(_: &Path) -> Result { + Err(()) + } + + pub fn status(&self, _: &Path) -> String { + panic!("Tried to access a Git repo without Git support!"); + } + + pub fn dir_status(&self, path: &Path) -> String { + self.status(path) + } +} diff --git a/src/xattr/xattr_darwin.rs b/src/feature/xattr_darwin.rs similarity index 85% rename from src/xattr/xattr_darwin.rs rename to src/feature/xattr_darwin.rs index cc51107..4d4021a 100644 --- a/src/xattr/xattr_darwin.rs +++ b/src/feature/xattr_darwin.rs @@ -41,7 +41,7 @@ pub struct Attribute { impl Attribute { /// Lists the extended attribute of `path`. /// Does follow symlinks by default. - pub fn list(path: &Path, flags: &[ListFlags]) -> io::IoResult> { + pub fn list_attrs(path: &Path, flags: &[ListFlags]) -> io::IoResult> { let mut c_flags: c_int = 0; for &flag in flags.iter() { c_flags |= flag as c_int @@ -112,19 +112,20 @@ impl Attribute { pub fn size(&self) -> usize { self.size } + + /// Lists the extended attributes. + /// Follows symlinks like `stat` + pub fn list(path: &Path) -> io::IoResult> { + Attribute::list_attrs(path, &[]) + } + /// Lists the extended attributes. + /// Does not follow symlinks like `lstat` + pub fn llist(path: &Path) -> io::IoResult> { + Attribute::list_attrs(path, &[ListFlags::NoFollow]) + } + + /// Returns true if the extended attribute feature is implemented on this platform. + #[inline(always)] + pub fn feature_implemented() -> bool { true } } -/// Lists the extended attributes. -/// Follows symlinks like `stat` -pub fn list(path: &Path) -> io::IoResult> { - Attribute::list(path, &[]) -} -/// Lists the extended attributes. -/// Does not follow symlinks like `lstat` -pub fn llist(path: &Path) -> io::IoResult> { - Attribute::list(path, &[ListFlags::NoFollow]) -} - -/// Returns true if the extended attribute feature is implemented on this platform. -#[inline(always)] -pub fn feature_implemented() -> bool { true } diff --git a/src/xattr/xattr_linux.rs b/src/feature/xattr_linux.rs similarity index 83% rename from src/xattr/xattr_linux.rs rename to src/feature/xattr_linux.rs index 47c368e..0aef938 100644 --- a/src/xattr/xattr_linux.rs +++ b/src/feature/xattr_linux.rs @@ -36,7 +36,7 @@ pub struct Attribute { impl Attribute { /// Lists the extended attribute of `path`. /// Does follow symlinks by default. - pub fn list(path: &Path, do_follow: FollowSymlinks) -> io::IoResult> { + pub fn list_attrs(path: &Path, do_follow: FollowSymlinks) -> io::IoResult> { let (listxattr, getxattr) = match do_follow { FollowSymlinks::Yes => (listxattr, getxattr), FollowSymlinks::No => (llistxattr, lgetxattr), @@ -103,19 +103,19 @@ impl Attribute { pub fn size(&self) -> usize { self.size } -} -/// Lists the extended attributes. -/// Follows symlinks like `stat` -pub fn list(path: &Path) -> io::IoResult> { - Attribute::list(path, FollowSymlinks::Yes) -} -/// Lists the extended attributes. -/// Does not follow symlinks like `lstat` -pub fn llist(path: &Path) -> io::IoResult> { - Attribute::list(path, FollowSymlinks::No) -} + /// Lists the extended attributes. + /// Follows symlinks like `stat` + pub fn list(path: &Path) -> io::IoResult> { + Attribute::list_attrs(path, FollowSymlinks::Yes) + } + /// Lists the extended attributes. + /// Does not follow symlinks like `lstat` + pub fn llist(path: &Path) -> io::IoResult> { + Attribute::list_attrs(path, FollowSymlinks::No) + } -/// Returns true if the extended attribute feature is implemented on this platform. -#[inline(always)] -pub fn feature_implemented() -> bool { true } + /// Returns true if the extended attribute feature is implemented on this platform. + #[inline(always)] + pub fn feature_implemented() -> bool { true } +} diff --git a/src/file.rs b/src/file.rs index ef968ae..a226f56 100644 --- a/src/file.rs +++ b/src/file.rs @@ -31,11 +31,10 @@ use column::Column::*; use dir::Dir; use filetype::HasType; use options::{SizeFormat, TimeType}; -use xattr; -use xattr::Attribute; +use feature::Attribute; /// This grey value is directly in between white and black, so it's guaranteed -/// to show up on either backgrounded terminal. +/// to show up on either backg"#160909"rounded terminal. pub static GREY: Colour = Fixed(244); /// A **File** is a wrapper around one of Rust's Path objects, along with @@ -83,7 +82,7 @@ impl<'a> File<'a> { dir: parent, stat: stat, ext: ext(&filename), - xattrs: xattr::llist(path).unwrap_or(Vec::new()), + xattrs: Attribute::llist(path).unwrap_or(Vec::new()), name: filename.to_string(), this: this, } @@ -227,7 +226,7 @@ impl<'a> File<'a> { dir: self.dir, stat: stat, ext: ext(&filename), - xattrs: xattr::list(target_path).unwrap_or(Vec::new()), + xattrs: Attribute::list(target_path).unwrap_or(Vec::new()), name: filename.to_string(), this: None, }) diff --git a/src/main.rs b/src/main.rs index eca253e..263d5c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,12 +33,12 @@ use output::lines_view; pub mod column; pub mod dir; +pub mod feature; pub mod file; pub mod filetype; pub mod options; pub mod output; pub mod term; -pub mod xattr; #[cfg(not(test))] struct Exa<'a> { diff --git a/src/options.rs b/src/options.rs index c29c20c..5a132cf 100644 --- a/src/options.rs +++ b/src/options.rs @@ -2,9 +2,9 @@ use dir::Dir; use file::File; use column::Column; use column::Column::*; +use feature::Attribute; use output::{Grid, Details}; use term::dimensions; -use xattr; use std::cmp::Ordering; use std::fmt; @@ -76,7 +76,7 @@ impl Options { opts.optflag("", "git", "show git status"); } - if xattr::feature_implemented() { + if Attribute::feature_implemented() { opts.optflag("@", "extended", "display extended attribute keys and sizes in long (-l) output"); } @@ -255,7 +255,7 @@ impl View { columns: try!(Columns::deduce(matches)), header: matches.opt_present("header"), recurse: dir_action.recurse_options().map(|o| (o, filter)), - xattr: xattr::feature_implemented() && matches.opt_present("extended"), + xattr: Attribute::feature_implemented() && matches.opt_present("extended"), }; Ok(View::Details(details)) @@ -294,7 +294,7 @@ impl View { else if matches.opt_present("level") && !matches.opt_present("recurse") { Err(Misfire::Useless2("level", "recurse", "tree")) } - else if xattr::feature_implemented() && matches.opt_present("extended") { + else if Attribute::feature_implemented() && matches.opt_present("extended") { Err(Misfire::Useless("extended", false, "long")) } else if matches.opt_present("oneline") { @@ -572,7 +572,7 @@ mod test { use super::Options; use super::Misfire; use super::Misfire::*; - use xattr; + use feature::Attribute; fn is_helpful(misfire: Result) -> bool { match misfire { @@ -674,7 +674,7 @@ mod test { #[test] fn extended_without_long() { - if xattr::feature_implemented() { + if Attribute::feature_implemented() { let opts = Options::getopts(&[ "--extended".to_string() ]); assert_eq!(opts.unwrap_err(), Misfire::Useless("extended", false, "long")) } diff --git a/src/output/details.rs b/src/output/details.rs index 1414e61..8e73e69 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -1,5 +1,5 @@ use column::{Alignment, Column, Cell}; -use xattr::Attribute; +use feature::Attribute; use dir::Dir; use file::{File, GREY}; use options::{Columns, FileFilter, RecurseOptions}; diff --git a/src/xattr/mod.rs b/src/xattr/mod.rs deleted file mode 100644 index edeb33b..0000000 --- a/src/xattr/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Extended attribute support -#[cfg(target_os = "macos")] -mod xattr_darwin; -#[cfg(target_os = "macos")] -pub use self::xattr_darwin::*; -#[cfg(target_os = "linux")] -mod xattr_linux; -#[cfg(target_os = "linux")] -pub use self::xattr_linux::*; -#[cfg(not(any(target_os = "macos", target_os = "linux")))] -mod xattr_other; -#[cfg(not(any(target_os = "macos", target_os = "linux")))] -pub use self::xattr_other::*; \ No newline at end of file diff --git a/src/xattr/xattr_other.rs b/src/xattr/xattr_other.rs deleted file mode 100644 index 4d28be0..0000000 --- a/src/xattr/xattr_other.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Extended attribute support for other os -use std::old_io as io; - -/// Extended attribute -#[derive(Clone)] -pub struct Attribute; - -impl Attribute { - - /// Getter for name - pub fn name(&self) -> &str { - unimplemented!() - } - - /// Getter for size - pub fn size(&self) -> usize { - unimplemented!() - } -} - -/// Lists the extended attributes. Follows symlinks like `stat` -pub fn list(_: &Path) -> io::IoResult> { - Ok(Vec::new()) -} -/// Lists the extended attributes. Does not follow symlinks like `lstat` -pub fn llist(_: &Path) -> io::IoResult> { - Ok(Vec::new()) -} - -/// Returns true if the extended attribute feature is implemented on this platform. -#[inline(always)] -pub fn feature_implemented() -> bool { false } \ No newline at end of file