add relative path function

This commit is contained in:
dvvvvvv 2020-02-04 13:32:36 +09:00 committed by Abin Simon
parent 746a929ebf
commit 3141278c65
4 changed files with 180 additions and 47 deletions

View file

@ -95,7 +95,12 @@ impl Core {
meta_list.push(meta);
}
_ => {
match meta.recurse_into(base_path, depth, self.flags.display, &self.flags.ignore_globs) {
match meta.recurse_into(
base_path,
depth,
self.flags.display,
&self.flags.ignore_globs,
) {
Ok(content) => {
meta.content = content;
meta_list.push(meta);

View file

@ -1,11 +1,10 @@
use crate::color::{ColoredString, Colors};
use crate::flags::{Block, Display, Flags, Layout};
use crate::icon::Icons;
use crate::meta::{FileType, Meta};
use crate::meta::name::DisplayOption;
use crate::meta::{FileType, Meta};
use ansi_term::{ANSIString, ANSIStrings};
use std::collections::HashMap;
use std::path::Path;
use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use terminal_size::terminal_size;
use unicode_width::UnicodeWidthStr;
@ -23,7 +22,17 @@ pub fn grid(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> St
let current_dir = std::env::current_dir().unwrap();
inner_display_grid(&current_dir, metas, &flags, colors, icons, 0, term_width)
inner_display_grid(
&DisplayOption::Relative {
base_path: &current_dir,
},
metas,
&flags,
colors,
icons,
0,
term_width,
)
}
pub fn tree(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> String {
@ -31,7 +40,7 @@ pub fn tree(metas: &[Meta], flags: &Flags, colors: &Colors, icons: &Icons) -> St
}
fn inner_display_grid(
base_path: &Path,
display_option: &DisplayOption,
metas: &[Meta],
flags: &Flags,
colors: &Colors,
@ -59,15 +68,20 @@ fn inner_display_grid(
let skip_dirs = (depth == 0) && (flags.display != Display::DisplayDirectoryItself);
// print the files first.
for (index, meta) in metas.iter().enumerate() {
for meta in metas.iter() {
// Maybe skip showing the directory meta now; show its contents later.
if let (true, FileType::Directory { .. }) = (skip_dirs, meta.file_type) {
continue;
}
let display_option = DisplayOption::Relative{ base_path };
let blocks = get_output(&meta, &colors, &icons, &flags, &display_option, &padding_rules);
let blocks = get_output(
&meta,
&colors,
&icons,
&flags,
&display_option,
&padding_rules,
);
for block in blocks {
let block_str = block.to_string();
@ -105,8 +119,16 @@ fn inner_display_grid(
output += &display_folder_path(&meta);
}
let display_option = if should_display_folder_path {
DisplayOption::FileName
} else {
DisplayOption::Relative {
base_path: &meta.path,
}
};
output += &inner_display_grid(
&meta.path,
&display_option,
meta.content.as_ref().unwrap(),
&flags,
colors,
@ -139,7 +161,14 @@ fn inner_display_tree(
});
for meta in metas.iter() {
for block in get_output(&meta, &colors, &icons, &flags, &DisplayOption::FileName, &padding_rules) {
for block in get_output(
&meta,
&colors,
&icons,
&flags,
&DisplayOption::FileName,
&padding_rules,
) {
let block_str = block.to_string();
grid.add(Cell {
@ -346,7 +375,7 @@ mod tests {
let output = name.render(
&Colors::new(color::Theme::NoColor),
&Icons::new(icon::Theme::NoIcon),
&DisplayOption::FileName
&DisplayOption::FileName,
);
assert_eq!(get_visible_width(&output), *l);
@ -378,7 +407,7 @@ mod tests {
.render(
&Colors::new(color::Theme::NoColor),
&Icons::new(icon::Theme::Fancy),
&DisplayOption::FileName
&DisplayOption::FileName,
)
.to_string();
@ -410,7 +439,7 @@ mod tests {
.render(
&Colors::new(color::Theme::NoLscolors),
&Icons::new(icon::Theme::NoIcon),
&DisplayOption::FileName
&DisplayOption::FileName,
)
.to_string();
@ -446,7 +475,7 @@ mod tests {
.render(
&Colors::new(color::Theme::NoColor),
&Icons::new(icon::Theme::NoIcon),
&DisplayOption::FileName
&DisplayOption::FileName,
)
.to_string();

View file

@ -96,8 +96,10 @@ impl Icons {
res += icon;
res += ICON_SPACE;
res
} else if let Some(icon) = name.extension()
.and_then(|extension| self.icons_by_extension.get(extension)){
} else if let Some(icon) = name
.extension()
.and_then(|extension| self.icons_by_extension.get(extension))
{
// Use the known extensions.
res += icon;
res += ICON_SPACE;

View file

@ -2,12 +2,13 @@ use crate::color::{ColoredString, Colors, Elem};
use crate::icon::Icons;
use crate::meta::filetype::FileType;
use std::cmp::{Ordering, PartialOrd};
use std::path::{Path, PathBuf, Component};
use std::ffi::OsStr;
use std::path::{Component, Path, PathBuf};
#[derive(Debug)]
pub enum DisplayOption<'a> {
FileName,
Relative{base_path: &'a Path},
Relative { base_path: &'a Path },
}
#[derive(Clone, Debug, Eq)]
@ -23,18 +24,43 @@ impl Name {
self.path.file_name().and_then(OsStr::to_str).unwrap()
}
fn relative_path(&self, base_path: &Path) -> std::borrow::Cow<'_, str> {
if self.path.starts_with(Component::ParentDir) {
self.path.to_string_lossy()
} else if self.path.starts_with(base_path) {
let relative_path = self.path.strip_prefix(base_path).unwrap().to_string_lossy();
if relative_path == "" { std::borrow::Cow::Borrowed(".")
} else {
relative_path
}
fn relative_path<T: AsRef<Path> + Clone>(&self, base_path: T) -> PathBuf {
use std::borrow::Cow;
let target_path = if self.path.is_absolute() {
Cow::Borrowed(&self.path)
} else {
std::borrow::Cow::Borrowed(&self.display_name)
Cow::Owned(self.path.canonicalize().unwrap())
};
let base_path = if base_path.as_ref().is_absolute() {
Cow::Borrowed(base_path.as_ref())
} else {
Cow::Owned(base_path.as_ref().canonicalize().unwrap())
};
if target_path == base_path {
return PathBuf::from("");
}
let shared_components: PathBuf = target_path
.components()
.zip(base_path.components())
.take_while(|(target_component, base_component)| target_component == base_component)
.map(|tuple| tuple.0)
.collect();
base_path
.strip_prefix(&shared_components)
.unwrap()
.components()
.map(|_| Component::ParentDir)
.chain(
target_path
.strip_prefix(&shared_components)
.unwrap()
.components(),
)
.collect()
}
pub fn new(path: &Path, file_type: FileType) -> Self {
@ -50,15 +76,28 @@ impl Name {
Self {
path: PathBuf::from(path),
extension,
display_name: path.file_name().and_then(OsStr::to_str).unwrap_or("?").to_owned(),
display_name: path
.file_name()
.and_then(OsStr::to_str)
.unwrap_or("?")
.to_owned(),
file_type,
}
}
pub fn render(&self, colors: &Colors, icons: &Icons, display_option: &DisplayOption) -> ColoredString {
pub fn render(
&self,
colors: &Colors,
icons: &Icons,
display_option: &DisplayOption,
) -> ColoredString {
let content = match display_option {
DisplayOption::FileName => format!("{}{}", icons.get(self), self.file_name()),
DisplayOption::Relative{base_path} => format!("{}{}", icons.get(self), self.relative_path(base_path)),
DisplayOption::Relative { base_path } => format!(
"{}{}",
icons.get(self),
self.relative_path(base_path).to_string_lossy()
),
};
let elem = match self.file_type {
@ -86,24 +125,31 @@ impl Name {
impl Ord for Name {
fn cmp(&self, other: &Self) -> Ordering {
self.display_name.cmp(&other.display_name)
self.display_name
.to_lowercase()
.cmp(&other.display_name.to_lowercase())
}
}
impl PartialOrd for Name {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.display_name.partial_cmp(&other.display_name)
self.display_name
.to_lowercase()
.partial_cmp(&other.display_name.to_lowercase())
}
}
impl PartialEq for Name {
fn eq(&self, other: &Self) -> bool {
self.path.eq(&other.path)
self.display_name
.to_lowercase()
.eq(&other.display_name.to_lowercase())
}
}
#[cfg(test)]
mod test {
use super::DisplayOption;
use super::Name;
use crate::color::{self, Colors};
use crate::icon::{self, Icons};
@ -116,7 +162,7 @@ mod test {
use std::fs::{self, File};
#[cfg(unix)]
use std::os::unix::fs::symlink;
use std::path::Path;
use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::process::Command;
use tempfile::tempdir;
@ -138,7 +184,7 @@ mod test {
assert_eq!(
Colour::Fixed(184).paint(" file.txt"),
name.render(&colors, &icons)
name.render(&colors, &icons, &DisplayOption::FileName)
);
}
@ -156,7 +202,7 @@ mod test {
assert_eq!(
Colour::Fixed(33).paint(" directory"),
meta.name.render(&colors, &icons)
meta.name.render(&colors, &icons, &DisplayOption::FileName)
);
}
@ -179,11 +225,11 @@ mod test {
let colors = Colors::new(color::Theme::NoLscolors);
let file_type = FileType::new(&meta, &Permissions::from(&meta));
let name = Name::new(&tmp_dir.into_path(), &symlink_path, file_type);
let name = Name::new(&symlink_path, file_type);
assert_eq!(
Colour::Fixed(44).paint(" target.tmp"),
name.render(&colors, &icons)
name.render(&colors, &icons, &DisplayOption::FileName)
);
}
@ -205,11 +251,11 @@ mod test {
let colors = Colors::new(color::Theme::NoLscolors);
let file_type = FileType::new(&meta, &Permissions::from(&meta));
let name = Name::new(&tmp_dir.into_path(), &pipe_path, file_type);
let name = Name::new(&pipe_path, file_type);
assert_eq!(
Colour::Fixed(184).paint(" pipe.tmp"),
name.render(&colors, &icons)
name.render(&colors, &icons, &DisplayOption::FileName)
);
}
@ -227,7 +273,10 @@ mod test {
assert_eq!(
"file.txt",
meta.name.render(&colors, &icons).to_string().as_str()
meta.name
.render(&colors, &icons, &DisplayOption::FileName)
.to_string()
.as_str()
);
}
@ -263,7 +312,7 @@ mod test {
#[test]
fn test_order_impl_is_case_insensitive() {
let path_1 = Path::new("AAAA");
let path_1 = Path::new("/AAAA");
let name_1 = Name::new(
&path_1,
FileType::File {
@ -272,7 +321,7 @@ mod test {
},
);
let path_2 = Path::new("aaaa");
let path_2 = Path::new("/aaaa");
let name_2 = Name::new(
&path_2,
FileType::File {
@ -286,7 +335,7 @@ mod test {
#[test]
fn test_partial_order_impl() {
let path_a = Path::new("aaaa");
let path_a = Path::new("/aaaa");
let name_a = Name::new(
&path_a,
FileType::File {
@ -295,7 +344,7 @@ mod test {
},
);
let path_z = Path::new("zzzz");
let path_z = Path::new("/zzzz");
let name_z = Name::new(
&path_z,
FileType::File {
@ -375,4 +424,52 @@ mod test {
assert_eq!(true, name_1 == name_2);
}
#[test]
fn test_parent_relative_path() {
let name = Name::new(
Path::new("/home/parent1/child"),
FileType::File {
uid: false,
exec: false,
},
);
let base_path = Path::new("/home/parent2");
assert_eq!(
PathBuf::from("../parent1/child"),
name.relative_path(base_path),
)
}
#[test]
fn test_current_relative_path() {
let name = Name::new(
Path::new("/home/parent1/child"),
FileType::File {
uid: false,
exec: false,
},
);
let base_path = PathBuf::from("/home/parent1");
assert_eq!(PathBuf::from("child"), name.relative_path(base_path),)
}
#[test]
fn test_grand_parent_relative_path() {
let name = Name::new(
Path::new("/home/grand-parent1/parent1/child"),
FileType::File {
uid: false,
exec: false,
},
);
let base_path = PathBuf::from("/home/grand-parent2/parent1");
assert_eq!(
PathBuf::from("../../grand-parent1/parent1/child"),
name.relative_path(base_path),
)
}
}