mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-09-28 05:09:01 +00:00
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.
This commit is contained in:
parent
697e1e66e4
commit
2ffa64cff6
105
src/dir.rs
105
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.
|
||||
@ -73,101 +72,3 @@ impl Dir {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Git, git2::Error> {
|
||||
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<Git, ()> {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
85
src/feature/git.rs
Normal file
85
src/feature/git.rs
Normal file
@ -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<Git, git2::Error> {
|
||||
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("-"),
|
||||
}
|
||||
}
|
||||
}
|
62
src/feature/mod.rs
Normal file
62
src/feature/mod.rs
Normal file
@ -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<Vec<Attribute>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Lists the extended attributes. Does not follow symlinks like `lstat`
|
||||
pub fn llist(_: &Path) -> io::IoResult<Vec<Attribute>> {
|
||||
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<Git, ()> {
|
||||
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)
|
||||
}
|
||||
}
|
@ -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<Vec<Attribute>> {
|
||||
pub fn list_attrs(path: &Path, flags: &[ListFlags]) -> io::IoResult<Vec<Attribute>> {
|
||||
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<Vec<Attribute>> {
|
||||
Attribute::list(path, &[])
|
||||
Attribute::list_attrs(path, &[])
|
||||
}
|
||||
/// Lists the extended attributes.
|
||||
/// Does not follow symlinks like `lstat`
|
||||
pub fn llist(path: &Path) -> io::IoResult<Vec<Attribute>> {
|
||||
Attribute::list(path, &[ListFlags::NoFollow])
|
||||
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 }
|
||||
}
|
||||
|
@ -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<Vec<Attribute>> {
|
||||
pub fn list_attrs(path: &Path, do_follow: FollowSymlinks) -> io::IoResult<Vec<Attribute>> {
|
||||
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<Vec<Attribute>> {
|
||||
Attribute::list(path, FollowSymlinks::Yes)
|
||||
Attribute::list_attrs(path, FollowSymlinks::Yes)
|
||||
}
|
||||
/// Lists the extended attributes.
|
||||
/// Does not follow symlinks like `lstat`
|
||||
pub fn llist(path: &Path) -> io::IoResult<Vec<Attribute>> {
|
||||
Attribute::list(path, FollowSymlinks::No)
|
||||
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 }
|
||||
}
|
@ -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,
|
||||
})
|
||||
|
@ -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> {
|
||||
|
@ -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<T>(misfire: Result<T, Misfire>) -> 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"))
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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::*;
|
@ -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<Vec<Attribute>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
/// Lists the extended attributes. Does not follow symlinks like `lstat`
|
||||
pub fn llist(_: &Path) -> io::IoResult<Vec<Attribute>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
/// Returns true if the extended attribute feature is implemented on this platform.
|
||||
#[inline(always)]
|
||||
pub fn feature_implemented() -> bool { false }
|
Loading…
Reference in New Issue
Block a user