mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-29 23:23:53 +00:00
Match up fields with parameter names
The arguments passed to File’s constructor were different from the field names used — these might as well both be the same. Also, move ext and filename to be File methods to save an import, and add tests. Also also, by passing a PathBuf in to the constructor directly, we can save one (possibly two) instance/s where we pass in a reference to something we were going to lose ownership of anyway, only to have it basically cloned.
This commit is contained in:
parent
30f74b08b4
commit
31148eda7b
@ -24,7 +24,7 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::{stderr, Write, Result as IOResult};
|
use std::io::{stderr, Write, Result as IOResult};
|
||||||
use std::path::{Component, Path};
|
use std::path::{Component, PathBuf};
|
||||||
|
|
||||||
use ansi_term::{ANSIStrings, Style};
|
use ansi_term::{ANSIStrings, Style};
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ impl<'w, W: Write + 'w> Exa<'w, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for file_name in &self.args {
|
for file_name in &self.args {
|
||||||
match File::new(Path::new(&file_name), None, None) {
|
match File::new(PathBuf::from(file_name), None, None) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
exit_status = 2;
|
exit_status = 2;
|
||||||
writeln!(stderr(), "{}: {}", file_name, e)?;
|
writeln!(stderr(), "{}: {}", file_name, e)?;
|
||||||
|
@ -112,14 +112,12 @@ impl<'dir> Files<'dir> {
|
|||||||
/// Go through the directory until we encounter a file we can list (which
|
/// Go through the directory until we encounter a file we can list (which
|
||||||
/// varies depending on the dotfile visibility flag)
|
/// varies depending on the dotfile visibility flag)
|
||||||
fn next_visible_file(&mut self) -> Option<Result<File<'dir>, (PathBuf, io::Error)>> {
|
fn next_visible_file(&mut self) -> Option<Result<File<'dir>, (PathBuf, io::Error)>> {
|
||||||
use fs::file::path_filename;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Some(path) = self.inner.next() {
|
if let Some(path) = self.inner.next() {
|
||||||
let filen = path_filename(path);
|
let filen = File::filename(path);
|
||||||
if !self.dotfiles && filen.starts_with(".") { continue }
|
if !self.dotfiles && filen.starts_with(".") { continue }
|
||||||
|
|
||||||
return Some(File::new(path, Some(self.dir), Some(filen))
|
return Some(File::new(path.clone(), Some(self.dir), Some(filen))
|
||||||
.map_err(|e| (path.clone(), e)))
|
.map_err(|e| (path.clone(), e)))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -150,12 +148,12 @@ impl<'dir> Iterator for Files<'dir> {
|
|||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if let Dots::DotNext = self.dots {
|
if let Dots::DotNext = self.dots {
|
||||||
self.dots = Dots::DotDotNext;
|
self.dots = Dots::DotDotNext;
|
||||||
Some(File::new(&self.dir.path, Some(self.dir), Some(String::from(".")))
|
Some(File::new(self.dir.path.to_path_buf(), Some(self.dir), Some(String::from(".")))
|
||||||
.map_err(|e| (Path::new(".").to_path_buf(), e)))
|
.map_err(|e| (Path::new(".").to_path_buf(), e)))
|
||||||
}
|
}
|
||||||
else if let Dots::DotDotNext = self.dots {
|
else if let Dots::DotDotNext = self.dots {
|
||||||
self.dots = Dots::FilesNext;
|
self.dots = Dots::FilesNext;
|
||||||
Some(File::new(&self.parent(), Some(self.dir), Some(String::from("..")))
|
Some(File::new(self.parent(), Some(self.dir), Some(String::from("..")))
|
||||||
.map_err(|e| (self.parent(), e)))
|
.map_err(|e| (self.parent(), e)))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
149
src/fs/file.rs
149
src/fs/file.rs
@ -53,34 +53,48 @@ pub struct File<'dir> {
|
|||||||
/// However, *directories* that get passed in will produce files that
|
/// However, *directories* that get passed in will produce files that
|
||||||
/// contain a reference to it, which is used in certain operations (such
|
/// contain a reference to it, which is used in certain operations (such
|
||||||
/// as looking up a file's Git status).
|
/// as looking up a file's Git status).
|
||||||
pub dir: Option<&'dir Dir>,
|
pub parent_dir: Option<&'dir Dir>,
|
||||||
}
|
|
||||||
|
|
||||||
/// A file’s name is derived from its string. This needs to handle directories
|
|
||||||
/// such as `/` or `..`, which have no `file_name` component. So instead, just
|
|
||||||
/// use the last component as the name.
|
|
||||||
pub fn path_filename(path: &Path) -> String {
|
|
||||||
match path.components().next_back() {
|
|
||||||
Some(back) => back.as_os_str().to_string_lossy().to_string(),
|
|
||||||
None => path.display().to_string(), // use the path as fallback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'dir> File<'dir> {
|
impl<'dir> File<'dir> {
|
||||||
pub fn new(path: &Path, parent: Option<&'dir Dir>, mut filename: Option<String>) -> IOResult<File<'dir>> {
|
pub fn new(path: PathBuf, parent_dir: Option<&'dir Dir>, mut filename: Option<String>) -> IOResult<File<'dir>> {
|
||||||
if filename.is_none() {
|
if filename.is_none() {
|
||||||
filename = Some(path_filename(path));
|
filename = Some(File::filename(&path));
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = fs::symlink_metadata(path)?;
|
let metadata = fs::symlink_metadata(&path)?;
|
||||||
|
let ext = File::ext(&path);
|
||||||
|
|
||||||
Ok(File {
|
Ok(File { path, parent_dir, metadata, ext, name: filename.unwrap() })
|
||||||
path: path.to_path_buf(),
|
}
|
||||||
dir: parent,
|
|
||||||
metadata: metadata,
|
/// A file’s name is derived from its string. This needs to handle directories
|
||||||
ext: ext(path),
|
/// such as `/` or `..`, which have no `file_name` component. So instead, just
|
||||||
name: filename.unwrap(),
|
/// use the last component as the name.
|
||||||
})
|
pub fn filename(path: &Path) -> String {
|
||||||
|
match path.components().next_back() {
|
||||||
|
Some(back) => back.as_os_str().to_string_lossy().to_string(),
|
||||||
|
None => path.display().to_string(), // use the path as fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract an extension from a file path, if one is present, in lowercase.
|
||||||
|
///
|
||||||
|
/// The extension is the series of characters after the last dot. This
|
||||||
|
/// deliberately counts dotfiles, so the ".git" folder has the extension "git".
|
||||||
|
///
|
||||||
|
/// ASCII lowercasing is used because these extensions are only compared
|
||||||
|
/// against a pre-compiled list of extensions which are known to only exist
|
||||||
|
/// within ASCII, so it's alright.
|
||||||
|
fn ext(path: &Path) -> Option<String> {
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
|
let name = match path.file_name() {
|
||||||
|
Some(f) => f.to_string_lossy().to_string(),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this file is a directory on the filesystem.
|
/// Whether this file is a directory on the filesystem.
|
||||||
@ -145,7 +159,7 @@ impl<'dir> File<'dir> {
|
|||||||
if path.is_absolute() {
|
if path.is_absolute() {
|
||||||
path.to_path_buf()
|
path.to_path_buf()
|
||||||
}
|
}
|
||||||
else if let Some(dir) = self.dir {
|
else if let Some(dir) = self.parent_dir {
|
||||||
dir.join(&*path)
|
dir.join(&*path)
|
||||||
}
|
}
|
||||||
else if let Some(parent) = self.path.parent() {
|
else if let Some(parent) = self.path.parent() {
|
||||||
@ -172,26 +186,22 @@ impl<'dir> File<'dir> {
|
|||||||
// this file -- which could be absolute or relative -- to the path
|
// this file -- which could be absolute or relative -- to the path
|
||||||
// we actually look up and turn into a `File` -- which needs to be
|
// we actually look up and turn into a `File` -- which needs to be
|
||||||
// absolute to be accessible from any directory.
|
// absolute to be accessible from any directory.
|
||||||
let display_path = match fs::read_link(&self.path) {
|
let path = match fs::read_link(&self.path) {
|
||||||
Ok(path) => path,
|
Ok(p) => p,
|
||||||
Err(e) => return FileTarget::Err(e),
|
Err(e) => return FileTarget::Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_path = self.reorient_target_path(&*display_path);
|
let absolute_path = self.reorient_target_path(&path);
|
||||||
|
|
||||||
// Use plain `metadata` instead of `symlink_metadata` - we *want* to
|
// Use plain `metadata` instead of `symlink_metadata` - we *want* to
|
||||||
// follow links.
|
// follow links.
|
||||||
if let Ok(metadata) = fs::metadata(&target_path) {
|
if let Ok(metadata) = fs::metadata(&absolute_path) {
|
||||||
FileTarget::Ok(File {
|
let ext = File::ext(&path);
|
||||||
dir: None,
|
let name = File::filename(&path);
|
||||||
ext: ext(&display_path),
|
FileTarget::Ok(File { parent_dir: None, path, ext, metadata, name })
|
||||||
metadata: metadata,
|
|
||||||
name: path_filename(&display_path),
|
|
||||||
path: display_path,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FileTarget::Broken(display_path)
|
FileTarget::Broken(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,7 +365,7 @@ impl<'dir> File<'dir> {
|
|||||||
pub fn git_status(&self) -> f::Git {
|
pub fn git_status(&self) -> f::Git {
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
|
|
||||||
match self.dir {
|
match self.parent_dir {
|
||||||
None => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified },
|
None => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified },
|
||||||
Some(d) => {
|
Some(d) => {
|
||||||
let cwd = match current_dir() {
|
let cwd = match current_dir() {
|
||||||
@ -377,26 +387,6 @@ impl<'a> AsRef<File<'a>> for File<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Extract an extension from a file path, if one is present, in lowercase.
|
|
||||||
///
|
|
||||||
/// The extension is the series of characters after the last dot. This
|
|
||||||
/// deliberately counts dotfiles, so the ".git" folder has the extension "git".
|
|
||||||
///
|
|
||||||
/// ASCII lowercasing is used because these extensions are only compared
|
|
||||||
/// against a pre-compiled list of extensions which are known to only exist
|
|
||||||
/// within ASCII, so it's alright.
|
|
||||||
fn ext(path: &Path) -> Option<String> {
|
|
||||||
use std::ascii::AsciiExt;
|
|
||||||
|
|
||||||
let name = match path.file_name() {
|
|
||||||
Some(f) => f.to_string_lossy().to_string(),
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The result of following a symlink.
|
/// The result of following a symlink.
|
||||||
pub enum FileTarget<'dir> {
|
pub enum FileTarget<'dir> {
|
||||||
|
|
||||||
@ -458,22 +448,59 @@ mod modes {
|
|||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod ext_test {
|
||||||
use super::ext;
|
use super::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extension() {
|
fn extension() {
|
||||||
assert_eq!(Some("dat".to_string()), ext(Path::new("fester.dat")))
|
assert_eq!(Some("dat".to_string()), File::ext(Path::new("fester.dat")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dotfile() {
|
fn dotfile() {
|
||||||
assert_eq!(Some("vimrc".to_string()), ext(Path::new(".vimrc")))
|
assert_eq!(Some("vimrc".to_string()), File::ext(Path::new(".vimrc")))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_extension() {
|
fn no_extension() {
|
||||||
assert_eq!(None, ext(Path::new("jarlsberg")))
|
assert_eq!(None, File::ext(Path::new("jarlsberg")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod filename_test {
|
||||||
|
use super::File;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn file() {
|
||||||
|
assert_eq!("fester.dat", File::filename(Path::new("fester.dat")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_path() {
|
||||||
|
assert_eq!("foo.wha", File::filename(Path::new("/var/cache/foo.wha")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn here() {
|
||||||
|
assert_eq!(".", File::filename(Path::new(".")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn there() {
|
||||||
|
assert_eq!("..", File::filename(Path::new("..")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn everywhere() {
|
||||||
|
assert_eq!("..", File::filename(Path::new("./..")))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn topmost() {
|
||||||
|
assert_eq!("/", File::filename(Path::new("/")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ impl<'a> File<'a> {
|
|||||||
if self.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc" ]) {
|
if self.extension_is_one_of( &[ "class", "elc", "hi", "o", "pyc" ]) {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else if let Some(dir) = self.dir {
|
else if let Some(dir) = self.parent_dir {
|
||||||
self.get_source_files().iter().any(|path| dir.contains(path))
|
self.get_source_files().iter().any(|path| dir.contains(path))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -49,7 +49,7 @@ impl<'a, 'dir> FileName<'a, 'dir> {
|
|||||||
pub fn paint(&self) -> TextCellContents {
|
pub fn paint(&self) -> TextCellContents {
|
||||||
let mut bits = Vec::new();
|
let mut bits = Vec::new();
|
||||||
|
|
||||||
if self.file.dir.is_none() {
|
if self.file.parent_dir.is_none() {
|
||||||
if let Some(parent) = self.file.path.parent() {
|
if let Some(parent) = self.file.path.parent() {
|
||||||
self.add_parent_bits(&mut bits, parent);
|
self.add_parent_bits(&mut bits, parent);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user