diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..c7ad93b --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +disable_all_formatting = true diff --git a/build.rs b/build.rs index 083126e..239d762 100644 --- a/build.rs +++ b/build.rs @@ -11,8 +11,9 @@ /// - https://crates.io/crates/vergen extern crate datetime; -use std::io::Result as IOResult; use std::env; +use std::io::Result as IOResult; + fn git_hash() -> String { use std::process::Command; @@ -52,7 +53,8 @@ fn write_statics() -> IOResult<()> { let ver = if is_development_version() { format!("exa v{} ({} built on {})", cargo_version(), git_hash(), build_date()) - } else { + } + else { format!("exa v{}", cargo_version()) }; diff --git a/src/fs/dir.rs b/src/fs/dir.rs index a02c3e2..a1e93c6 100644 --- a/src/fs/dir.rs +++ b/src/fs/dir.rs @@ -10,7 +10,7 @@ use log::*; use crate::fs::File; -/// A **Dir** provides a cached list of the file paths in a directory that's +/// A **Dir** provides a cached list of the file paths in a directory that’s /// being listed. /// /// This object gets passed to the Files themselves, in order for them to @@ -39,8 +39,8 @@ impl Dir { info!("Reading directory {:?}", &path); let contents = fs::read_dir(&path)? - .map(|result| result.map(|entry| entry.path())) - .collect::>()?; + .map(|result| result.map(|entry| entry.path())) + .collect::>()?; Ok(Self { contents, path }) } @@ -53,7 +53,8 @@ impl Dir { dir: self, dotfiles: dots.shows_dotfiles(), dots: dots.dots(), - git, git_ignoring, + git, + git_ignoring, } } @@ -106,7 +107,9 @@ impl<'dir, 'ig> Files<'dir, 'ig> { loop { if let Some(path) = self.inner.next() { let filename = File::filename(path); - if !self.dotfiles && filename.starts_with('.') { continue } + if ! self.dotfiles && filename.starts_with('.') { + continue; + } if self.git_ignoring { let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default(); @@ -139,7 +142,6 @@ enum DotsNext { Files, } - impl<'dir, 'ig> Iterator for Files<'dir, 'ig> { type Item = Result, (PathBuf, io::Error)>; @@ -149,22 +151,24 @@ impl<'dir, 'ig> Iterator for Files<'dir, 'ig> { self.dots = DotsNext::DotDot; Some(File::new_aa_current(self.dir) .map_err(|e| (Path::new(".").to_path_buf(), e))) - }, + } + DotsNext::DotDot => { self.dots = DotsNext::Files; Some(File::new_aa_parent(self.parent(), self.dir) .map_err(|e| (self.parent(), e))) - }, + } + DotsNext::Files => { self.next_visible_file() - }, + } } } } /// Usually files in Unix use a leading dot to be hidden or visible, but two -/// entries in particular are "extra-hidden": `.` and `..`, which only become +/// entries in particular are “extra-hidden”: `.` and `..`, which only become /// visible after an extra `-a` option. #[derive(PartialEq, Debug, Copy, Clone)] pub enum DotFilter { @@ -199,9 +203,9 @@ impl DotFilter { /// Whether this filter should add dot directories to a listing. fn dots(self) -> DotsNext { match self { - Self::JustFiles => DotsNext::Files, - Self::Dotfiles => DotsNext::Files, - Self::DotfilesAndDots => DotsNext::Dot, + Self::JustFiles => DotsNext::Files, + Self::Dotfiles => DotsNext::Files, + Self::DotfilesAndDots => DotsNext::Dot, } } } diff --git a/src/fs/feature/git.rs b/src/fs/feature/git.rs index 490f18b..88fcbc0 100644 --- a/src/fs/feature/git.rs +++ b/src/fs/feature/git.rs @@ -36,7 +36,9 @@ impl GitCache { use std::iter::FromIterator; impl FromIterator for GitCache { - fn from_iter>(iter: I) -> Self { + fn from_iter(iter: I) -> Self + where I: IntoIterator + { let iter = iter.into_iter(); let mut git = Self { repos: Vec::with_capacity(iter.size_hint().0), @@ -61,8 +63,10 @@ impl FromIterator for GitCache { debug!("Discovered new Git repo"); git.repos.push(r); - }, - Err(miss) => git.misses.push(miss), + } + Err(miss) => { + git.misses.push(miss) + } } } } @@ -72,8 +76,6 @@ impl FromIterator for GitCache { } - - /// A **Git repository** is one we’ve discovered somewhere on the filesystem. pub struct GitRepo { @@ -99,7 +101,9 @@ pub struct GitRepo { enum GitContents { /// All the interesting Git stuff goes through this. - Before { repo: git2::Repository }, + Before { + repo: git2::Repository, + }, /// Temporary value used in `repo_to_statuses` so we can move the /// repository out of the `Before` variant. @@ -107,7 +111,9 @@ enum GitContents { /// The data we’ve extracted from the repository, but only after we’ve /// actually done so. - After { statuses: Git } + After { + statuses: Git, + }, } impl GitRepo { @@ -116,7 +122,7 @@ impl GitRepo { /// depending on the prefix-lookup flag) and returns its Git status. /// /// Actually querying the `git2` repository for the mapping of paths to - /// Git statuses is only done once, and gets cached so we don't need to + /// Git statuses is only done once, and gets cached so we don’t need to /// re-query the entire repository the times after that. /// /// The temporary `Processing` enum variant is used after the `git2` @@ -166,7 +172,7 @@ impl GitRepo { let workdir = workdir.to_path_buf(); let contents = Mutex::new(GitContents::Before { repo }); Ok(Self { contents, workdir, original_path: path, extra_paths: Vec::new() }) - }, + } None => { warn!("Repository has no workdir?"); Err(path) @@ -205,8 +211,10 @@ fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git { let elem = (path, e.status()); statuses.push(elem); } - }, - Err(e) => error!("Error looking up Git statuses: {:?}", e), + } + Err(e) => { + error!("Error looking up Git statuses: {:?}", e) + } } Git { statuses } @@ -217,7 +225,7 @@ fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git { // 20.311276 INFO:exa::fs::feature::git: Getting Git statuses for repo with workdir "/vagrant/" // 20.799610 DEBUG:exa::output::table: Getting Git status for file "./Cargo.toml" // -// Even inserting another logging line immediately afterwards doesn't make it +// Even inserting another logging line immediately afterwards doesn’t make it // look any faster. @@ -239,6 +247,7 @@ impl Git { /// Get the status for the file at the given path. fn file_status(&self, file: &Path) -> f::Git { let path = reorient(file); + self.statuses.iter() .find(|p| p.0.as_path() == path) .map(|&(_, s)| f::Git { staged: index_status(s), unstaged: working_tree_status(s) }) @@ -250,25 +259,31 @@ impl Git { /// directories, which don’t really have an ‘official’ status. fn dir_status(&self, dir: &Path) -> f::Git { let path = reorient(dir); - let s = self.statuses.iter() - .filter(|p| p.0.starts_with(&path)) - .fold(git2::Status::empty(), |a, b| a | b.1); - f::Git { staged: index_status(s), unstaged: working_tree_status(s) } + let s = self.statuses.iter() + .filter(|p| p.0.starts_with(&path)) + .fold(git2::Status::empty(), |a, b| a | b.1); + + let staged = index_status(s); + let unstaged = working_tree_status(s); + f::Git { staged, unstaged } } } + /// Converts a path to an absolute path based on the current directory. /// Paths need to be absolute for them to be compared properly, otherwise /// you’d ask a repo about “./README.md” but it only knows about /// “/vagrant/README.md”, prefixed by the workdir. fn reorient(path: &Path) -> PathBuf { use std::env::current_dir; - // I’m not 100% on this func tbh + + // TODO: I’m not 100% on this func tbh let path = match current_dir() { - Err(_) => Path::new(".").join(&path), - Ok(dir) => dir.join(&path), + Err(_) => Path::new(".").join(&path), + Ok(dir) => dir.join(&path), }; + path.canonicalize().unwrap_or(path) } diff --git a/src/fs/feature/mod.rs b/src/fs/feature/mod.rs index 62c76c1..c4fc20e 100644 --- a/src/fs/feature/mod.rs +++ b/src/fs/feature/mod.rs @@ -1,8 +1,9 @@ pub mod xattr; -#[cfg(feature="git")] pub mod git; +#[cfg(feature = "git")] +pub mod git; -#[cfg(not(feature="git"))] +#[cfg(not(feature = "git"))] pub mod git { use std::iter::FromIterator; use std::path::{Path, PathBuf}; @@ -13,8 +14,10 @@ pub mod git { pub struct GitCache; impl FromIterator for GitCache { - fn from_iter>(_iter: I) -> Self { - GitCache + fn from_iter(_iter: I) -> Self + where I: IntoIterator + { + Self } } diff --git a/src/fs/feature/xattr.rs b/src/fs/feature/xattr.rs index ea718b4..acf3098 100644 --- a/src/fs/feature/xattr.rs +++ b/src/fs/feature/xattr.rs @@ -1,4 +1,5 @@ //! Extended attribute support for Darwin and Linux systems. + #![allow(trivial_casts)] // for ARM extern crate libc; @@ -6,7 +7,11 @@ use std::cmp::Ordering; use std::io; use std::path::Path; -pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux")); + +pub const ENABLED: bool = + cfg!(feature="git") && + cfg!(any(target_os = "macos", target_os = "linux")); + pub trait FileAttributes { fn attributes(&self) -> io::Result>; @@ -27,20 +32,21 @@ impl FileAttributes for Path { #[cfg(not(any(target_os = "macos", target_os = "linux")))] impl FileAttributes for Path { fn attributes(&self) -> io::Result> { - Ok(vec![]) + Ok(Vec::new()) } fn symlink_attributes(&self) -> io::Result> { - Ok(vec![]) + Ok(Vec::new()) } } + /// Attributes which can be passed to `Attribute::list_with_flags` #[cfg(any(target_os = "macos", target_os = "linux"))] #[derive(Copy, Clone)] pub enum FollowSymlinks { Yes, - No + No, } /// Extended attribute @@ -50,29 +56,32 @@ pub struct Attribute { pub size: usize, } + #[cfg(any(target_os = "macos", target_os = "linux"))] pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result> { use std::ffi::CString; - let c_path = match path.to_str().and_then(|s| { CString::new(s).ok() }) { + let c_path = match path.to_str().and_then(|s| CString::new(s).ok()) { Some(cstring) => cstring, - None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")), + None => { + return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")); + } }; let bufsize = lister.listxattr_first(&c_path); match bufsize.cmp(&0) { - Ordering::Less => return Err(io::Error::last_os_error()), - Ordering::Equal => return Ok(Vec::new()), - Ordering::Greater => {}, + Ordering::Less => return Err(io::Error::last_os_error()), + Ordering::Equal => return Ok(Vec::new()), + Ordering::Greater => {}, } let mut buf = vec![0_u8; bufsize as usize]; let err = lister.listxattr_second(&c_path, &mut buf, bufsize); match err.cmp(&0) { - Ordering::Less => return Err(io::Error::last_os_error()), - Ordering::Equal => return Ok(Vec::new()), - Ordering::Greater => {}, + Ordering::Less => return Err(io::Error::last_os_error()), + Ordering::Equal => return Ok(Vec::new()), + Ordering::Greater => {}, } let mut names = Vec::new(); @@ -91,33 +100,40 @@ pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result 0 { names.push(Attribute { name: lister.translate_attribute_name(&buf[start..end]), - size: size as usize + size: size as usize, }); } start = c_end; } } + Ok(names) } + #[cfg(target_os = "macos")] mod lister { - use std::ffi::CString; - use libc::{c_int, size_t, ssize_t, c_char, c_void}; use super::FollowSymlinks; + use libc::{c_int, size_t, ssize_t, c_char, c_void}; + use std::ffi::CString; use std::ptr; extern "C" { fn listxattr( - path: *const c_char, namebuf: *mut c_char, - size: size_t, options: c_int + path: *const c_char, + namebuf: *mut c_char, + size: size_t, + options: c_int, ) -> ssize_t; fn getxattr( - path: *const c_char, name: *const c_char, - value: *mut c_void, size: size_t, position: u32, - options: c_int + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, + position: u32, + options: c_int, ) -> ssize_t; } @@ -128,24 +144,25 @@ mod lister { impl Lister { pub fn new(do_follow: FollowSymlinks) -> Self { let c_flags: c_int = match do_follow { - FollowSymlinks::Yes => 0x0001, - FollowSymlinks::No => 0x0000, + FollowSymlinks::Yes => 0x0001, + FollowSymlinks::No => 0x0000, }; Self { c_flags } } pub fn translate_attribute_name(&self, input: &[u8]) -> String { - use std::str::from_utf8_unchecked; - - unsafe { - from_utf8_unchecked(input).into() - } + unsafe { std::str::from_utf8_unchecked(input).into() } } pub fn listxattr_first(&self, c_path: &CString) -> ssize_t { unsafe { - listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags) + listxattr( + c_path.as_ptr(), + ptr::null_mut(), + 0, + self.c_flags, + ) } } @@ -154,7 +171,8 @@ mod lister { listxattr( c_path.as_ptr(), buf.as_mut_ptr() as *mut c_char, - bufsize as size_t, self.c_flags + bufsize as size_t, + self.c_flags, ) } } @@ -164,13 +182,17 @@ mod lister { getxattr( c_path.as_ptr(), buf.as_ptr() as *const c_char, - ptr::null_mut(), 0, 0, self.c_flags + ptr::null_mut(), + 0, + 0, + self.c_flags, ) } } } } + #[cfg(target_os = "linux")] mod lister { use std::ffi::CString; @@ -180,21 +202,29 @@ mod lister { extern "C" { fn listxattr( - path: *const c_char, list: *mut c_char, size: size_t + path: *const c_char, + list: *mut c_char, + size: size_t, ) -> ssize_t; fn llistxattr( - path: *const c_char, list: *mut c_char, size: size_t + path: *const c_char, + list: *mut c_char, + size: size_t, ) -> ssize_t; fn getxattr( - path: *const c_char, name: *const c_char, - value: *mut c_void, size: size_t + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, ) -> ssize_t; fn lgetxattr( - path: *const c_char, name: *const c_char, - value: *mut c_void, size: size_t + path: *const c_char, + name: *const c_char, + value: *mut c_void, + size: size_t, ) -> ssize_t; } @@ -213,41 +243,46 @@ mod lister { pub fn listxattr_first(&self, c_path: &CString) -> ssize_t { let listxattr = match self.follow_symlinks { - FollowSymlinks::Yes => listxattr, - FollowSymlinks::No => llistxattr, + FollowSymlinks::Yes => listxattr, + FollowSymlinks::No => llistxattr, }; unsafe { - listxattr(c_path.as_ptr() as *const _, ptr::null_mut(), 0) + listxattr( + c_path.as_ptr() as *const _, + ptr::null_mut(), + 0, + ) } } pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec, bufsize: ssize_t) -> ssize_t { let listxattr = match self.follow_symlinks { - FollowSymlinks::Yes => listxattr, - FollowSymlinks::No => llistxattr, + FollowSymlinks::Yes => listxattr, + FollowSymlinks::No => llistxattr, }; unsafe { listxattr( c_path.as_ptr() as *const _, buf.as_mut_ptr() as *mut c_char, - bufsize as size_t + bufsize as size_t, ) } } pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t { let getxattr = match self.follow_symlinks { - FollowSymlinks::Yes => getxattr, - FollowSymlinks::No => lgetxattr, + FollowSymlinks::Yes => getxattr, + FollowSymlinks::No => lgetxattr, }; unsafe { getxattr( c_path.as_ptr() as *const _, buf.as_ptr() as *const c_char, - ptr::null_mut(), 0 + ptr::null_mut(), + 0, ) } } diff --git a/src/fs/fields.rs b/src/fs/fields.rs index 4d3cb86..434a164 100644 --- a/src/fs/fields.rs +++ b/src/fs/fields.rs @@ -1,4 +1,3 @@ - //! Wrapper types for the values returned from `File`s. //! //! The methods of `File` that return information about the entry on the @@ -45,7 +44,14 @@ pub type uid_t = u32; /// Its ordering is used when sorting by type. #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub enum Type { - Directory, File, Link, Pipe, Socket, CharDevice, BlockDevice, Special, + Directory, + File, + Link, + Pipe, + Socket, + CharDevice, + BlockDevice, + Special, } impl Type { @@ -152,7 +158,7 @@ pub enum Size { /// have a file size. For example, a directory will just contain a list of /// its files as its “contents” and will be specially flagged as being a /// directory, rather than a file. However, seeing the “file size” of this - /// data is rarely useful -- I can’t think of a time when I’ve seen it and + /// data is rarely useful — I can’t think of a time when I’ve seen it and /// learnt something. So we discard it and just output “-” instead. /// /// See this answer for more: http://unix.stackexchange.com/a/68266 @@ -214,10 +220,11 @@ pub enum GitStatus { /// A file that’s ignored (that matches a line in .gitignore) Ignored, - /// A file that's updated but unmerged. + /// A file that’s updated but unmerged. Conflicted, } + /// A file’s complete Git status. It’s possible to make changes to a file, add /// it to the staging area, then make *more* changes, so we need to list each /// file’s status for both of these. @@ -227,11 +234,13 @@ pub struct Git { pub unstaged: GitStatus, } -use std::default::Default; impl Default for Git { /// Create a Git status for a file with nothing done to it. fn default() -> Self { - Self { staged: GitStatus::NotModified, unstaged: GitStatus::NotModified } + Self { + staged: GitStatus::NotModified, + unstaged: GitStatus::NotModified, + } } } diff --git a/src/fs/file.rs b/src/fs/file.rs index 5657588..dce5219 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -2,7 +2,7 @@ use std::io::Error as IOError; use std::io::Result as IOResult; -use std::os::unix::fs::{MetadataExt, PermissionsExt, FileTypeExt}; +use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -11,7 +11,8 @@ use log::*; use crate::fs::dir::Dir; use crate::fs::fields as f; -/// A **File** is a wrapper around one of Rust's Path objects, along with + +/// A **File** is a wrapper around one of Rust’s `PathBuf` values, along with /// associated data about the file. /// /// Each file is definitely going to have its filename displayed at least @@ -44,7 +45,7 @@ pub struct File<'dir> { /// /// This too is queried multiple times, and is *not* cached by the OS, as /// it could easily change between invocations — but exa is so short-lived - /// it's better to just cache it. + /// it’s better to just cache it. pub metadata: std::fs::Metadata, /// A reference to the directory that contains this file, if any. @@ -60,7 +61,7 @@ pub struct File<'dir> { /// Whether this is one of the two `--all all` directories, `.` and `..`. /// /// Unlike all other entries, these are not returned as part of the - /// directory's children, and are in fact added specifically by exa; this + /// directory’s children, and are in fact added specifically by exa; this /// means that they should be skipped when recursing. pub is_all_all: bool, } @@ -88,8 +89,9 @@ impl<'dir> File<'dir> { debug!("Statting file {:?}", &path); let metadata = std::fs::symlink_metadata(&path)?; let is_all_all = true; + let parent_dir = Some(parent_dir); - Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: ".".to_string(), is_all_all }) + Ok(File { path, parent_dir, metadata, ext, name: ".".into(), is_all_all }) } pub fn new_aa_parent(path: PathBuf, parent_dir: &'dir Dir) -> IOResult> { @@ -98,8 +100,9 @@ impl<'dir> File<'dir> { debug!("Statting file {:?}", &path); let metadata = std::fs::symlink_metadata(&path)?; let is_all_all = true; + let parent_dir = Some(parent_dir); - Ok(File { path, parent_dir: Some(parent_dir), metadata, ext, name: "..".to_string(), is_all_all }) + Ok(File { path, parent_dir, metadata, ext, name: "..".into(), is_all_all }) } /// A file’s name is derived from its string. This needs to handle directories @@ -127,7 +130,9 @@ impl<'dir> File<'dir> { fn ext(path: &Path) -> Option { let name = path.file_name().map(|f| f.to_string_lossy().to_string())?; - name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase()) + name.rfind('.') + .map(|p| name[p + 1 ..] + .to_ascii_lowercase()) } /// Whether this file is a directory on the filesystem. @@ -249,7 +254,8 @@ impl<'dir> File<'dir> { Ok(metadata) => { let ext = File::ext(&path); let name = File::filename(&path); - FileTarget::Ok(Box::new(File { parent_dir: None, path, ext, metadata, name, is_all_all: false })) + let file = File { parent_dir: None, path, ext, metadata, name, is_all_all: false }; + FileTarget::Ok(Box::new(file)) } Err(e) => { error!("Error following link {:?}: {:#?}", &path, e); @@ -274,14 +280,14 @@ impl<'dir> File<'dir> { } } - /// This file's inode. + /// This file’s inode. pub fn inode(&self) -> f::Inode { f::Inode(self.metadata.ino()) } - /// This file's number of filesystem blocks. + /// This file’s number of filesystem blocks. /// - /// (Not the size of each block, which we don't actually report on) + /// (Not the size of each block, which we don’t actually report on) pub fn blocks(&self) -> f::Blocks { if self.is_file() || self.is_link() { f::Blocks::Some(self.metadata.blocks()) @@ -334,17 +340,19 @@ impl<'dir> File<'dir> { pub fn changed_time(&self) -> Option { let (mut sec, mut nsec) = (self.metadata.ctime(), self.metadata.ctime_nsec()); - Some( - if sec < 0 { - if nsec > 0 { - sec += 1; - nsec -= 1_000_000_000; - } - UNIX_EPOCH - Duration::new(sec.abs() as u64, nsec.abs() as u32) - } else { - UNIX_EPOCH + Duration::new(sec as u64, nsec as u32) - } - ) + if sec < 0 { + if nsec > 0 { + sec += 1; + nsec -= 1_000_000_000; + } + + let duration = Duration::new(sec.abs() as u64, nsec.abs() as u32); + Some(UNIX_EPOCH - duration) + } + else { + let duration = Duration::new(sec as u64, nsec as u32); + Some(UNIX_EPOCH + duration) + } } /// This file’s last accessed timestamp, if available on this platform. @@ -392,7 +400,7 @@ impl<'dir> File<'dir> { /// This file’s permissions, with flags for each bit. pub fn permissions(&self) -> f::Permissions { let bits = self.metadata.mode(); - let has_bit = |bit| { bits & bit == bit }; + let has_bit = |bit| bits & bit == bit; f::Permissions { user_read: has_bit(modes::USER_READ), @@ -423,7 +431,7 @@ impl<'dir> File<'dir> { } } - /// Whether this file's name, including extension, is any of the strings + /// Whether this file’s name, including extension, is any of the strings /// that get passed in. pub fn name_is_one_of(&self, choices: &[&str]) -> bool { choices.contains(&&self.name[..]) @@ -455,7 +463,7 @@ pub enum FileTarget<'dir> { // Err is its own variant, instead of having the whole thing be inside an // `IOResult`, because being unable to follow a symlink is not a serious - // error -- we just display the error message and move on. + // error — we just display the error message and move on. } impl<'dir> FileTarget<'dir> { @@ -471,9 +479,10 @@ impl<'dir> FileTarget<'dir> { /// More readable aliases for the permission bits exposed by libc. #[allow(trivial_numeric_casts)] mod modes { - pub type Mode = u32; + // The `libc::mode_t` type’s actual type varies, but the value returned // from `metadata.permissions().mode()` is always `u32`. + pub type Mode = u32; pub const USER_READ: Mode = libc::S_IRUSR as Mode; pub const USER_WRITE: Mode = libc::S_IWUSR as Mode; diff --git a/src/fs/filter.rs b/src/fs/filter.rs index 26c5385..a902798 100644 --- a/src/fs/filter.rs +++ b/src/fs/filter.rs @@ -5,8 +5,8 @@ use std::iter::FromIterator; use std::os::unix::fs::MetadataExt; use std::path::Path; -use crate::fs::File; use crate::fs::DotFilter; +use crate::fs::File; /// The **file filter** processes a list of files before displaying them to @@ -88,12 +88,11 @@ pub struct FileFilter { pub git_ignore: GitIgnore, } - impl FileFilter { /// Remove every file in the given vector that does *not* pass the /// filter predicate for files found inside a directory. pub fn filter_child_files(&self, files: &mut Vec) { - files.retain(|f| !self.ignore_patterns.is_ignored(&f.name)); + files.retain(|f| ! self.ignore_patterns.is_ignored(&f.name)); if self.only_dirs { files.retain(File::is_directory); @@ -110,14 +109,18 @@ impl FileFilter { /// `exa -I='*.ogg' music/*` should filter out the ogg files obtained /// from the glob, even though the globbing is done by the shell! pub fn filter_argument_files(&self, files: &mut Vec) { - files.retain(|f| !self.ignore_patterns.is_ignored(&f.name)); + files.retain(|f| { + ! self.ignore_patterns.is_ignored(&f.name) + }); } /// Sort the files in the given vector based on the sort field option. pub fn sort_files<'a, F>(&self, files: &mut Vec) - where F: AsRef> { - - files.sort_by(|a, b| self.sort_field.compare_files(a.as_ref(), b.as_ref())); + where F: AsRef> + { + files.sort_by(|a, b| { + self.sort_field.compare_files(a.as_ref(), b.as_ref()) + }); if self.reverse { files.reverse(); @@ -126,8 +129,9 @@ impl FileFilter { if self.list_dirs_first { // This relies on the fact that `sort_by` is *stable*: it will keep // adjacent elements next to each other. - files.sort_by(|a, b| {b.as_ref().points_to_directory() - .cmp(&a.as_ref().points_to_directory()) + files.sort_by(|a, b| { + b.as_ref().points_to_directory() + .cmp(&a.as_ref().points_to_directory()) }); } } @@ -175,13 +179,13 @@ pub enum SortField { /// The time the file was changed (the “ctime”). /// /// This field is used to mark the time when a file’s metadata - /// changed -- its permissions, owners, or link count. + /// changed — its permissions, owners, or link count. /// /// In original Unix, this was, however, meant as creation time. /// https://www.bell-labs.com/usr/dmr/www/cacm.html ChangedDate, - /// The time the file was created (the "btime" or "birthtime"). + /// The time the file was created (the “btime” or “birthtime”). CreatedDate, /// The type of the file: directories, links, pipes, regular, files, etc. @@ -280,11 +284,8 @@ impl SortField { } fn strip_dot(n: &str) -> &str { - if n.starts_with('.') { - &n[1..] - } else { - n - } + if n.starts_with('.') { &n[1..] } + else { n } } } @@ -298,8 +299,12 @@ pub struct IgnorePatterns { } impl FromIterator for IgnorePatterns { - fn from_iter>(iter: I) -> Self { - Self { patterns: iter.into_iter().collect() } + + fn from_iter(iter: I) -> Self + where I: IntoIterator + { + let patterns = iter.into_iter().collect(); + Self { patterns } } } @@ -371,7 +376,6 @@ pub enum GitIgnore { // > usually found in $XDG_CONFIG_HOME/git/ignore. - #[cfg(test)] mod test_ignores { use super::*; diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 3275ccf..1188f61 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -4,7 +4,7 @@ pub use self::dir::{Dir, DotFilter}; mod file; pub use self::file::{File, FileTarget}; +pub mod dir_action; pub mod feature; pub mod fields; pub mod filter; -pub mod dir_action; diff --git a/src/info/filetype.rs b/src/info/filetype.rs index 3c5afeb..a23a39f 100644 --- a/src/info/filetype.rs +++ b/src/info/filetype.rs @@ -124,11 +124,17 @@ impl FileIcon for FileExtensions { fn icon_file(&self, file: &File) -> Option { use crate::output::icons::Icons; - Some(match file { - f if self.is_music(f) || self.is_lossless(f) => Icons::Audio.value(), - f if self.is_image(f) => Icons::Image.value(), - f if self.is_video(f) => Icons::Video.value(), - _ => return None, - }) + if self.is_music(file) || self.is_lossless(file) { + Some(Icons::Audio.value()) + } + else if self.is_image(file) { + Some(Icons::Image.value()) + } + else if self.is_video(file) { + Some(Icons::Video.value()) + } + else { + None + } } } diff --git a/src/info/sources.rs b/src/info/sources.rs index 042fdb4..fac5aa1 100644 --- a/src/info/sources.rs +++ b/src/info/sources.rs @@ -11,7 +11,7 @@ impl<'a> File<'a> { /// The point of this is to highlight compiled files such as `foo.js` when /// their source file `foo.coffee` exists in the same directory. /// For example, `foo.js` is perfectly valid without `foo.coffee`, so we - /// don't want to always blindly highlight `*.js` as compiled. + /// don’t want to always blindly highlight `*.js` as compiled. /// (See also `FileExtensions#is_compiled`) pub fn get_source_files(&self) -> Vec { if let Some(ext) = &self.ext { @@ -34,7 +34,7 @@ impl<'a> File<'a> { } } else { - vec![] // No source files if there's no extension, either! + vec![] // No source files if there’s no extension, either! } } } diff --git a/src/logger.rs b/src/logger.rs index 4f89eb1..b3c33c5 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -57,10 +57,10 @@ impl log::Log for Logger { fn level(level: log::Level) -> ANSIString<'static> { match level { - log::Level::Error => Colour::Red.paint("ERROR"), - log::Level::Warn => Colour::Yellow.paint("WARN"), - log::Level::Info => Colour::Cyan.paint("INFO"), - log::Level::Debug => Colour::Blue.paint("DEBUG"), - log::Level::Trace => Colour::Fixed(245).paint("TRACE"), + log::Level::Error => Colour::Red.paint("ERROR"), + log::Level::Warn => Colour::Yellow.paint("WARN"), + log::Level::Info => Colour::Cyan.paint("INFO"), + log::Level::Debug => Colour::Blue.paint("DEBUG"), + log::Level::Trace => Colour::Fixed(245).paint("TRACE"), } } diff --git a/src/main.rs b/src/main.rs index 3b4df40..52b7795 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,10 @@ use ansi_term::{ANSIStrings, Style}; use log::*; use crate::fs::{Dir, File}; -use crate::fs::filter::GitIgnore; use crate::fs::feature::git::GitCache; +use crate::fs::filter::GitIgnore; use crate::options::{Options, Vars}; -pub use crate::options::vars; -pub use crate::options::Misfire; +pub use crate::options::{Misfire, vars}; use crate::output::{escape, lines, grid, grid_details, details, View, Mode}; mod fs; @@ -35,18 +34,24 @@ fn main() { match Exa::from_args(args.iter(), stdout()) { Ok(mut exa) => { match exa.run() { - Ok(exit_status) => exit(exit_status), + Ok(exit_status) => { + exit(exit_status) + } + Err(e) => { match e.kind() { - ErrorKind::BrokenPipe => exit(exits::SUCCESS), + ErrorKind::BrokenPipe => { + exit(exits::SUCCESS); + } + _ => { eprintln!("{}", e); exit(exits::RUNTIME_ERROR); - }, + } }; } }; - }, + } Err(ref e) if e.is_error() => { let mut stderr = stderr(); @@ -57,13 +62,13 @@ fn main() { } exit(exits::OPTIONS_ERROR); - }, + } Err(ref e) => { println!("{}", e); exit(exits::SUCCESS); - }, - }; + } + } } @@ -109,7 +114,7 @@ fn git_options(options: &Options, args: &[&OsStr]) -> Option { impl<'args> Exa<'args> { pub fn from_args(args: I, writer: Stdout) -> Result, Misfire> - where I: Iterator + where I: Iterator { let (options, mut args) = Options::parse(args, &LiveVars)?; debug!("Dir action from arguments: {:#?}", options.dir_action); @@ -136,18 +141,19 @@ impl<'args> Exa<'args> { Err(e) => { exit_status = 2; writeln!(stderr(), "{:?}: {}", file_path, e)?; - }, + } + Ok(f) => { - if f.points_to_directory() && !self.options.dir_action.treat_dirs_as_files() { + if f.points_to_directory() && ! self.options.dir_action.treat_dirs_as_files() { match f.to_dir() { - Ok(d) => dirs.push(d), - Err(e) => writeln!(stderr(), "{:?}: {}", file_path, e)?, + Ok(d) => dirs.push(d), + Err(e) => writeln!(stderr(), "{:?}: {}", file_path, e)?, } } else { files.push(f); } - }, + } } } @@ -176,7 +182,7 @@ impl<'args> Exa<'args> { writeln!(&mut self.writer)?; } - if !is_only_dir { + if ! is_only_dir { let mut bits = Vec::new(); escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default()); writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?; @@ -196,10 +202,10 @@ impl<'args> Exa<'args> { if let Some(recurse_opts) = self.options.dir_action.recurse_options() { let depth = dir.path.components().filter(|&c| c != Component::CurDir).count() + 1; - if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) { + if ! recurse_opts.tree && ! recurse_opts.is_too_deep(depth) { let mut child_dirs = Vec::new(); - for child_dir in children.iter().filter(|f| f.is_directory() && !f.is_all_all) { + for child_dir in children.iter().filter(|f| f.is_directory() && ! f.is_all_all) { match child_dir.to_dir() { Ok(d) => child_dirs.push(d), Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?, @@ -225,43 +231,42 @@ impl<'args> Exa<'args> { /// For various annoying logistical reasons, each one handles /// printing differently... fn print_files(&mut self, dir: Option<&Dir>, files: Vec) -> IOResult<()> { - if !files.is_empty() { - let View { ref mode, ref colours, ref style } = self.options.view; - - match mode { - Mode::Lines(ref opts) => { - let r = lines::Render { files, colours, style, opts }; - r.render(&mut self.writer) - } - - Mode::Grid(ref opts) => { - let r = grid::Render { files, colours, style, opts }; - r.render(&mut self.writer) - } - - Mode::Details(ref opts) => { - let filter = &self.options.filter; - let recurse = self.options.dir_action.recurse_options(); - - let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; - let r = details::Render { dir, files, colours, style, opts, filter, recurse, git_ignoring }; - r.render(self.git.as_ref(), &mut self.writer) - } - - Mode::GridDetails(ref opts) => { - let grid = &opts.grid; - let filter = &self.options.filter; - let details = &opts.details; - let row_threshold = opts.row_threshold; - - let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; - let r = grid_details::Render { dir, files, colours, style, grid, details, filter, row_threshold, git_ignoring }; - r.render(self.git.as_ref(), &mut self.writer) - } - } + if files.is_empty() { + return Ok(()); } - else { - Ok(()) + + let View { ref mode, ref colours, ref style } = self.options.view; + + match mode { + Mode::Lines(ref opts) => { + let r = lines::Render { files, colours, style, opts }; + r.render(&mut self.writer) + } + + Mode::Grid(ref opts) => { + let r = grid::Render { files, colours, style, opts }; + r.render(&mut self.writer) + } + + Mode::Details(ref opts) => { + let filter = &self.options.filter; + let recurse = self.options.dir_action.recurse_options(); + + let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + let r = details::Render { dir, files, colours, style, opts, filter, recurse, git_ignoring }; + r.render(self.git.as_ref(), &mut self.writer) + } + + Mode::GridDetails(ref opts) => { + let grid = &opts.grid; + let filter = &self.options.filter; + let details = &opts.details; + let row_threshold = opts.row_threshold; + + let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore; + let r = grid_details::Render { dir, files, colours, style, grid, details, filter, row_threshold, git_ignoring }; + r.render(self.git.as_ref(), &mut self.writer) + } } } } diff --git a/src/options/dir_action.rs b/src/options/dir_action.rs index 7316ddf..c16cff2 100644 --- a/src/options/dir_action.rs +++ b/src/options/dir_action.rs @@ -19,7 +19,7 @@ impl DirAction { if matches.is_strict() { // Early check for --level when it wouldn’t do anything - if !recurse && !tree && matches.count(&flags::LEVEL) > 0 { + if ! recurse && ! tree && matches.count(&flags::LEVEL) > 0 { return Err(Misfire::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE)); } else if recurse && as_file { diff --git a/src/options/filter.rs b/src/options/filter.rs index 4d248e7..fd21b6f 100644 --- a/src/options/filter.rs +++ b/src/options/filter.rs @@ -12,13 +12,13 @@ impl FileFilter { /// Determines which of all the file filter options to use. pub fn deduce(matches: &MatchedFlags) -> Result { Ok(Self { - list_dirs_first: matches.has(&flags::DIRS_FIRST)?, - reverse: matches.has(&flags::REVERSE)?, - only_dirs: matches.has(&flags::ONLY_DIRS)?, - sort_field: SortField::deduce(matches)?, - dot_filter: DotFilter::deduce(matches)?, - ignore_patterns: IgnorePatterns::deduce(matches)?, - git_ignore: GitIgnore::deduce(matches)?, + list_dirs_first: matches.has(&flags::DIRS_FIRST)?, + reverse: matches.has(&flags::REVERSE)?, + only_dirs: matches.has(&flags::ONLY_DIRS)?, + sort_field: SortField::deduce(matches)?, + dot_filter: DotFilter::deduce(matches)?, + ignore_patterns: IgnorePatterns::deduce(matches)?, + git_ignore: GitIgnore::deduce(matches)?, }) } } @@ -42,29 +42,64 @@ impl SortField { }; let field = match word { - "name" | "filename" => Self::Name(SortCase::AaBbCc), - "Name" | "Filename" => Self::Name(SortCase::ABCabc), - ".name" | ".filename" => Self::NameMixHidden(SortCase::AaBbCc), - ".Name" | ".Filename" => Self::NameMixHidden(SortCase::ABCabc), - "size" | "filesize" => Self::Size, - "ext" | "extension" => Self::Extension(SortCase::AaBbCc), - "Ext" | "Extension" => Self::Extension(SortCase::ABCabc), + "name" | "filename" => { + Self::Name(SortCase::AaBbCc) + } + "Name" | "Filename" => { + Self::Name(SortCase::ABCabc) + } + ".name" | ".filename" => { + Self::NameMixHidden(SortCase::AaBbCc) + } + ".Name" | ".Filename" => { + Self::NameMixHidden(SortCase::ABCabc) + } + "size" | "filesize" => { + Self::Size + } + "ext" | "extension" => { + Self::Extension(SortCase::AaBbCc) + } + "Ext" | "Extension" => { + Self::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? - "date" | "time" | "mod" | "modified" | "new" | "newest" => Self::ModifiedDate, + "date" | "time" | "mod" | "modified" | "new" | "newest" => { + Self::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. - "age" | "old" | "oldest" => Self::ModifiedAge, - "ch" | "changed" => Self::ChangedDate, - "acc" | "accessed" => Self::AccessedDate, - "cr" | "created" => Self::CreatedDate, - "inode" => Self::FileInode, - "type" => Self::FileType, - "none" => Self::Unsorted, - _ => return Err(Misfire::BadArgument(&flags::SORT, word.into())) + "age" | "old" | "oldest" => { + Self::ModifiedAge + } + + "ch" | "changed" => { + Self::ChangedDate + } + "acc" | "accessed" => { + Self::AccessedDate + } + "cr" | "created" => { + Self::CreatedDate + } + "inode" => { + Self::FileInode + } + "type" => { + Self::FileType + } + "none" => { + Self::Unsorted + } + _ => { + return Err(Misfire::BadArgument(&flags::SORT, word.into())); + } }; Ok(field) @@ -103,7 +138,6 @@ impl SortField { // “apps” first, then “Documents”. // // You can get the old behaviour back by sorting with `--sort=Name`. - impl Default for SortField { fn default() -> Self { Self::Name(SortCase::AaBbCc) @@ -151,8 +185,8 @@ impl IgnorePatterns { // If there are no inputs, we return a set of patterns that doesn’t // match anything, rather than, say, `None`. let inputs = match matches.get(&flags::IGNORE_GLOB)? { - None => return Ok(Self::empty()), - Some(is) => is, + Some(is) => is, + None => return Ok(Self::empty()), }; // Awkwardly, though, a glob pattern can be invalid, and we need to @@ -162,8 +196,8 @@ impl IgnorePatterns { // It can actually return more than one glob error, // but we only use one. (TODO) match errors.pop() { - Some(e) => Err(e.into()), - None => Ok(patterns), + Some(e) => Err(e.into()), + None => Ok(patterns), } } } @@ -171,13 +205,16 @@ impl IgnorePatterns { impl GitIgnore { pub fn deduce(matches: &MatchedFlags) -> Result { - Ok(if matches.has(&flags::GIT_IGNORE)? { Self::CheckAndIgnore } - else { Self::Off }) + if matches.has(&flags::GIT_IGNORE)? { + Ok(Self::CheckAndIgnore) + } + else { + Ok(Self::Off) + } } } - #[cfg(test)] mod test { use super::*; diff --git a/src/options/flags.rs b/src/options/flags.rs index b4e24d2..88d53da 100644 --- a/src/options/flags.rs +++ b/src/options/flags.rs @@ -1,4 +1,4 @@ -use crate::options::parser::{Arg, Args, Values, TakesValue}; +use crate::options::parser::{Arg, Args, TakesValue, Values}; // exa options @@ -32,8 +32,8 @@ pub static GIT_IGNORE: Arg = Arg { short: None, long: "git-ignore", t pub static DIRS_FIRST: Arg = Arg { short: None, long: "group-directories-first", takes_value: TakesValue::Forbidden }; pub static ONLY_DIRS: Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden }; const SORTS: Values = &[ "name", "Name", "size", "extension", - "Extension", "modified", "changed", "accessed", - "created", "inode", "type", "none" ]; + "Extension", "modified", "changed", "accessed", + "created", "inode", "type", "none" ]; // display options pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden }; diff --git a/src/options/help.rs b/src/options/help.rs index d279c8c..4bb551c 100644 --- a/src/options/help.rs +++ b/src/options/help.rs @@ -1,8 +1,8 @@ use std::fmt; +use crate::fs::feature::xattr; use crate::options::flags; use crate::options::parser::MatchedFlags; -use crate::fs::feature::xattr; static OPTIONS: &str = r##" @@ -106,7 +106,7 @@ impl fmt::Display for HelpString { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { writeln!(f, "Usage:\n exa [options] [files...]")?; - if !self.only_long { + if ! self.only_long { write!(f, "{}", OPTIONS)?; } @@ -127,7 +127,6 @@ impl fmt::Display for HelpString { } - #[cfg(test)] mod test { use crate::options::Options; diff --git a/src/options/misfire.rs b/src/options/misfire.rs index b42294b..2bfae85 100644 --- a/src/options/misfire.rs +++ b/src/options/misfire.rs @@ -6,7 +6,7 @@ use crate::options::{flags, HelpString, VersionString}; use crate::options::parser::{Arg, Flag, ParseError}; -/// A **misfire** is a thing that can happen instead of listing files -- a +/// A **misfire** is a thing that can happen instead of listing files — a /// catch-all for anything outside the program’s normal execution. #[derive(PartialEq, Debug)] pub enum Misfire { @@ -34,7 +34,7 @@ pub enum Misfire { Conflict(&'static Arg, &'static Arg), /// An option was given that does nothing when another one either is or - /// isn't present. + /// isn’t present. Useless(&'static Arg, bool, &'static Arg), /// An option was given that does nothing when either of two other options @@ -78,19 +78,19 @@ impl fmt::Display for Misfire { write!(f, "Option {} has no {:?} setting", arg, attempt) } }, - Self::InvalidOptions(e) => write!(f, "{}", e), - Self::Unsupported(e) => write!(f, "{}", e), - Self::Help(text) => write!(f, "{}", text), - Self::Version(version) => write!(f, "{}", version), - Self::Conflict(a, b) => write!(f, "Option {} conflicts with option {}", a, b), - Self::Duplicate(a, b) if a == b => write!(f, "Flag {} was given twice", a), - Self::Duplicate(a, b) => write!(f, "Flag {} conflicts with flag {}", a, b), - Self::Useless(a, false, b) => write!(f, "Option {} is useless without option {}", a, b), - Self::Useless(a, true, b) => write!(f, "Option {} is useless given option {}", a, b), - Self::Useless2(a, b1, b2) => write!(f, "Option {} is useless without options {} or {}", a, b1, b2), - Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"), - Self::FailedParse(ref e) => write!(f, "Failed to parse number: {}", e), - Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e), + Self::InvalidOptions(e) => write!(f, "{}", e), + Self::Unsupported(e) => write!(f, "{}", e), + Self::Help(text) => write!(f, "{}", text), + Self::Version(version) => write!(f, "{}", version), + Self::Conflict(a, b) => write!(f, "Option {} conflicts with option {}", a, b), + Self::Duplicate(a, b) if a == b => write!(f, "Flag {} was given twice", a), + Self::Duplicate(a, b) => write!(f, "Flag {} conflicts with flag {}", a, b), + Self::Useless(a, false, b) => write!(f, "Option {} is useless without option {}", a, b), + Self::Useless(a, true, b) => write!(f, "Option {} is useless given option {}", a, b), + Self::Useless2(a, b1, b2) => write!(f, "Option {} is useless without options {} or {}", a, b1, b2), + Self::TreeAllAll => write!(f, "Option --tree is useless given --all --all"), + Self::FailedParse(ref e) => write!(f, "Failed to parse number: {}", e), + Self::FailedGlobPattern(ref e) => write!(f, "Failed to parse glob pattern: {}", e), } } } diff --git a/src/options/mod.rs b/src/options/mod.rs index dd9896c..ba25a0e 100644 --- a/src/options/mod.rs +++ b/src/options/mod.rs @@ -60,7 +60,7 @@ //! //! `--sort=size` should override `--sort=Name` because it’s closer to the end //! of the arguments array. In fact, because there’s no way to tell where the -//! arguments came from -- it’s just a heuristic -- this will still work even +//! arguments came from — it’s just a heuristic — this will still work even //! if no aliases are being used! //! //! Finally, this isn’t just useful when options could override each other. @@ -72,12 +72,13 @@ use std::ffi::{OsStr, OsString}; use crate::fs::dir_action::DirAction; -use crate::fs::filter::{FileFilter,GitIgnore}; +use crate::fs::filter::{FileFilter, GitIgnore}; use crate::output::{View, Mode, details, grid_details}; -mod style; mod dir_action; mod filter; +mod flags; +mod style; mod view; mod help; @@ -93,7 +94,6 @@ pub mod vars; pub use self::vars::Vars; mod parser; -mod flags; use self::parser::MatchedFlags; @@ -120,8 +120,9 @@ impl Options { /// for extra options. #[allow(unused_results)] pub fn parse<'args, I, V>(args: I, vars: &V) -> Result<(Self, Vec<&'args OsStr>), Misfire> - where I: IntoIterator, - V: Vars { + where I: IntoIterator, + V: Vars + { use crate::options::parser::{Matches, Strictness}; let strictness = match vars.get(vars::EXA_STRICT) { @@ -169,7 +170,6 @@ impl Options { } - #[cfg(test)] pub mod test { use super::{Options, Misfire, flags}; diff --git a/src/options/parser.rs b/src/options/parser.rs index 99f5ab8..7d2cd6c 100644 --- a/src/options/parser.rs +++ b/src/options/parser.rs @@ -70,8 +70,8 @@ impl Flag { impl fmt::Display for Flag { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { - Self::Short(short) => write!(f, "-{}", *short as char), - Self::Long(long) => write!(f, "--{}", long), + Self::Short(short) => write!(f, "-{}", *short as char), + Self::Long(long) => write!(f, "--{}", long), } } } @@ -144,7 +144,8 @@ impl Args { /// Iterates over the given list of command-line arguments and parses /// them into a list of matched flags and free strings. pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result, ParseError> - where I: IntoIterator { + where I: IntoIterator + { use std::os::unix::ffi::OsStrExt; let mut parsing = true; @@ -164,7 +165,7 @@ impl Args { // This allows a file named “--arg” to be specified by passing in // the pair “-- --arg”, without it getting matched as a flag that // doesn’t exist. - if !parsing { + if ! parsing { frees.push(arg) } else if arg == "--" { @@ -348,7 +349,7 @@ pub struct Matches<'args> { pub flags: MatchedFlags<'args>, /// All the strings that weren’t matched as arguments, as well as anything - /// after the special "--" string. + /// after the special “--” string. pub frees: Vec<&'args OsStr>, } @@ -373,7 +374,8 @@ impl<'a> MatchedFlags<'a> { /// Returns `true` if it was, `false` if it wasn’t, and an error in /// strict mode if it was specified more than once. pub fn has(&self, arg: &'static Arg) -> Result { - self.has_where(|flag| flag.matches(arg)).map(|flag| flag.is_some()) + self.has_where(|flag| flag.matches(arg)) + .map(|flag| flag.is_some()) } /// Returns the first found argument that satisfies the predicate, or @@ -488,7 +490,7 @@ fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> { let (before, after) = input.as_bytes().split_at(index); // The after string contains the = that we need to remove. - if !before.is_empty() && after.len() >= 2 { + if ! before.is_empty() && after.len() >= 2 { return Some((OsStr::from_bytes(before), OsStr::from_bytes(&after[1..]))) } @@ -569,7 +571,9 @@ mod parse_test { let strictness = Strictness::UseLastArguments; // this isn’t even used let got = Args(TEST_ARGS).parse(inputs.iter(), strictness); - let expected = Ok(Matches { frees, flags: MatchedFlags { flags, strictness } }); + let flags = MatchedFlags { flags, strictness }; + + let expected = Ok(Matches { frees, flags }); assert_eq!(got, expected); } }; @@ -580,9 +584,11 @@ mod parse_test { use self::ParseError::*; let strictness = Strictness::UseLastArguments; // this isn’t even used - let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::>(); - let got = Args(TEST_ARGS).parse(bits.iter(), strictness); + let bits = $inputs.as_ref().into_iter() + .map(|&o| os(o)) + .collect::>(); + let got = Args(TEST_ARGS).parse(bits.iter(), strictness); assert_eq!(got, Err($error)); } }; @@ -719,6 +725,6 @@ mod matches_test { fn no_count() { let flags = MatchedFlags { flags: Vec::new(), strictness: Strictness::UseLastArguments }; - assert!(!flags.has(&COUNT).unwrap()); + assert_eq!(flags.has(&COUNT).unwrap(), false); } } diff --git a/src/options/style.rs b/src/options/style.rs index 1dbddbc..c877276 100644 --- a/src/options/style.rs +++ b/src/options/style.rs @@ -3,7 +3,7 @@ use ansi_term::Style; use crate::fs::File; use crate::options::{flags, Vars, Misfire}; use crate::options::parser::MatchedFlags; -use crate::output::file_name::{FileStyle, Classify}; +use crate::output::file_name::{Classify, FileStyle}; use crate::style::Colours; @@ -38,10 +38,9 @@ impl TerminalColours { /// Determine which terminal colour conditions to use. fn deduce(matches: &MatchedFlags) -> Result { - let word = match matches.get_where(|f| f.matches(&flags::COLOR) || f.matches(&flags::COLOUR))? { - Some(w) => w, - None => return Ok(Self::default()), + Some(w) => w, + None => return Ok(Self::default()), }; if word == "always" { @@ -77,7 +76,7 @@ pub struct Styles { impl Styles { - #[allow(trivial_casts)] // the "as Box<_>" stuff below warns about this for some reason + #[allow(trivial_casts)] // the `as Box<_>` stuff below warns about this for some reason pub fn deduce(matches: &MatchedFlags, vars: &V, widther: TW) -> Result where TW: Fn() -> Option, V: Vars { use crate::info::filetype::FileExtensions; @@ -89,12 +88,12 @@ impl Styles { // custom colours at all let tc = TerminalColours::deduce(matches)?; - if tc == TerminalColours::Never - || (tc == TerminalColours::Automatic && widther().is_none()) - { + if tc == TerminalColours::Never || (tc == TerminalColours::Automatic && widther().is_none()) { + let exts = Box::new(NoFileColours); + return Ok(Self { colours: Colours::plain(), - style: FileStyle { classify, exts: Box::new(NoFileColours) }, + style: FileStyle { classify, exts }, }); } @@ -133,11 +132,16 @@ fn parse_color_vars(vars: &V, colours: &mut Colours) -> (ExtensionMappi if let Some(lsc) = vars.get(vars::LS_COLORS) { let lsc = lsc.to_string_lossy(); + LSColors(lsc.as_ref()).each_pair(|pair| { - if !colours.set_ls(&pair) { + if ! colours.set_ls(&pair) { match glob::Pattern::new(pair.key) { - Ok(pat) => exts.add(pat, pair.to_style()), - Err(e) => warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e), + Ok(pat) => { + exts.add(pat, pair.to_style()); + } + Err(e) => { + warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e); + } } } }); @@ -154,10 +158,14 @@ fn parse_color_vars(vars: &V, colours: &mut Colours) -> (ExtensionMappi } LSColors(exa.as_ref()).each_pair(|pair| { - if !colours.set_ls(&pair) && !colours.set_exa(&pair) { + if ! colours.set_ls(&pair) && ! colours.set_exa(&pair) { match glob::Pattern::new(pair.key) { - Ok(pat) => exts.add(pat, pair.to_style()), - Err(e) => warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e), + Ok(pat) => { + exts.add(pat, pair.to_style()); + } + Err(e) => { + warn!("Couldn't parse glob pattern {:?}: {}", pair.key, e); + } } }; }); @@ -169,7 +177,7 @@ fn parse_color_vars(vars: &V, colours: &mut Colours) -> (ExtensionMappi #[derive(PartialEq, Debug, Default)] struct ExtensionMappings { - mappings: Vec<(glob::Pattern, Style)> + mappings: Vec<(glob::Pattern, Style)>, } // Loop through backwards so that colours specified later in the list override @@ -178,9 +186,7 @@ struct ExtensionMappings { use crate::output::file_name::FileColours; impl FileColours for ExtensionMappings { fn colour_file(&self, file: &File) -> Option