mirror of
https://github.com/lsd-rs/lsd
synced 2024-12-16 23:03:07 +00:00
309 lines
8.8 KiB
Rust
309 lines
8.8 KiB
Rust
use color::{ColoredString, Colors, Elem};
|
|
use icon::Icons;
|
|
use meta::filetype::FileType;
|
|
use std::cmp::{Ordering, PartialOrd};
|
|
use std::path::Path;
|
|
|
|
#[derive(Debug, Eq)]
|
|
pub struct Name {
|
|
name: String,
|
|
extension: Option<String>,
|
|
file_type: FileType,
|
|
}
|
|
|
|
impl Name {
|
|
pub fn new(path: &Path, file_type: FileType) -> Self {
|
|
let name = path
|
|
.file_name()
|
|
.expect("failed to retrieve file name")
|
|
.to_string_lossy()
|
|
.to_string();
|
|
|
|
let mut extension = None;
|
|
if let Some(res) = path.extension() {
|
|
extension = Some(
|
|
res.to_str()
|
|
.expect("failed to encode file name")
|
|
.to_string(),
|
|
);
|
|
}
|
|
|
|
Self {
|
|
name,
|
|
extension,
|
|
file_type,
|
|
}
|
|
}
|
|
|
|
pub fn render(&self, colors: &Colors, icons: &Icons) -> ColoredString {
|
|
let icon = icons.get(self);
|
|
let mut content = String::with_capacity(icon.len() + self.name.len() + 3 /* spaces */);
|
|
|
|
content += icon.as_str();
|
|
|
|
let elem = match self.file_type {
|
|
FileType::CharDevice => &Elem::CharDevice,
|
|
FileType::Directory => &Elem::Dir,
|
|
FileType::SymLink => &Elem::SymLink,
|
|
FileType::ExecutableFile => &Elem::ExecutableFile,
|
|
_ => &Elem::File,
|
|
};
|
|
|
|
content += &self.name;
|
|
|
|
colors.colorize(content, elem)
|
|
}
|
|
|
|
pub fn name(&self) -> String {
|
|
self.name.clone()
|
|
}
|
|
|
|
pub fn extension(&self) -> Option<String> {
|
|
self.extension.clone()
|
|
}
|
|
|
|
pub fn file_type(&self) -> FileType {
|
|
self.file_type
|
|
}
|
|
|
|
pub fn is_hidden(&self) -> bool {
|
|
self.name.starts_with('.')
|
|
}
|
|
}
|
|
|
|
impl Ord for Name {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.name.to_lowercase().cmp(&other.name.to_lowercase())
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for Name {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.name.to_lowercase().cmp(&other.name.to_lowercase()))
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Name {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.name.eq_ignore_ascii_case(&other.name)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::Name;
|
|
use ansi_term::Colour;
|
|
use color::{self, Colors};
|
|
use icon::{self, Icons};
|
|
use meta::FileType;
|
|
use meta::Permissions;
|
|
use std::cmp::Ordering;
|
|
use std::fs::{self, File};
|
|
use std::os::unix::fs::symlink;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
use tempdir::TempDir;
|
|
|
|
#[test]
|
|
fn test_print_file_name() {
|
|
let tmp_dir = TempDir::new("test_print_file_name").expect("failed to create temp dir");
|
|
let icons = Icons::new(icon::Theme::Default);
|
|
|
|
// Create the file;
|
|
let file_path = tmp_dir.path().join("file.txt");
|
|
File::create(&file_path).expect("failed to create file");
|
|
let meta = file_path.metadata().expect("failed to get metas");
|
|
|
|
let colors = Colors::new(color::Theme::Default);
|
|
let file_type = FileType::new(&meta, &Permissions::from(&meta));
|
|
let name = Name::new(&file_path, file_type);
|
|
|
|
assert_eq!(
|
|
Colour::Fixed(184).paint(" file.txt"),
|
|
name.render(&colors, &icons)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_print_dir_name() {
|
|
let tmp_dir = TempDir::new("test_print_dir_name").expect("failed to create temp dir");
|
|
let icons = Icons::new(icon::Theme::Default);
|
|
|
|
// Chreate the directory
|
|
let dir_path = tmp_dir.path().join("directory");
|
|
fs::create_dir(&dir_path).expect("failed to create the dir");
|
|
let meta = dir_path.metadata().expect("failed to get metas");
|
|
|
|
let colors = Colors::new(color::Theme::Default);
|
|
let file_type = FileType::new(&meta, &Permissions::from(&meta));
|
|
let name = Name::new(&dir_path, file_type);
|
|
|
|
assert_eq!(
|
|
Colour::Fixed(33).paint(" directory"),
|
|
name.render(&colors, &icons)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_print_symlink_name() {
|
|
let tmp_dir = TempDir::new("test_symlink_name").expect("failed to create temp dir");
|
|
let icons = Icons::new(icon::Theme::Default);
|
|
|
|
// Create the file;
|
|
let file_path = tmp_dir.path().join("file.tmp");
|
|
File::create(&file_path).expect("failed to create file");
|
|
|
|
// Create the symlink
|
|
let symlink_path = tmp_dir.path().join("target.tmp");
|
|
symlink(&file_path, &symlink_path).expect("failed to create symlink");
|
|
let meta = symlink_path
|
|
.symlink_metadata()
|
|
.expect("failed to get metas");
|
|
|
|
let colors = Colors::new(color::Theme::Default);
|
|
let file_type = FileType::new(&meta, &Permissions::from(&meta));
|
|
let name = Name::new(&symlink_path, file_type);
|
|
|
|
assert_eq!(
|
|
Colour::Fixed(44).paint(" target.tmp"),
|
|
name.render(&colors, &icons)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_print_other_type_name() {
|
|
let tmp_dir = TempDir::new("test_other_type_name").expect("failed to create temp dir");
|
|
let icons = Icons::new(icon::Theme::Default);
|
|
|
|
// Create the pipe;
|
|
let pipe_path = tmp_dir.path().join("pipe.tmp");
|
|
let success = Command::new("mkfifo")
|
|
.arg(&pipe_path)
|
|
.status()
|
|
.expect("failed to exec mkfifo")
|
|
.success();
|
|
assert_eq!(true, success, "failed to exec mkfifo");
|
|
let meta = pipe_path.metadata().expect("failed to get metas");
|
|
|
|
let colors = Colors::new(color::Theme::Default);
|
|
let file_type = FileType::new(&meta, &Permissions::from(&meta));
|
|
let name = Name::new(&pipe_path, file_type);
|
|
|
|
assert_eq!(
|
|
Colour::Fixed(184).paint(" pipe.tmp"),
|
|
name.render(&colors, &icons)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_print_without_icon_or_color() {
|
|
let tmp_dir =
|
|
TempDir::new("test_print_without_icon_or_color").expect("failed to create temp dir");
|
|
let icons = Icons::new(icon::Theme::NoIcon);
|
|
|
|
// Create the file;
|
|
let file_path = tmp_dir.path().join("file.txt");
|
|
File::create(&file_path).expect("failed to create file");
|
|
let meta = file_path.metadata().expect("failed to get metas");
|
|
|
|
let colors = Colors::new(color::Theme::NoColor);
|
|
let file_type = FileType::new(&meta, &Permissions::from(&meta));
|
|
let name = Name::new(&file_path, file_type);
|
|
|
|
assert_eq!(
|
|
"file.txt",
|
|
name.render(&colors, &icons).to_string().as_str()
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_extensions_with_valid_file() {
|
|
let path = Path::new("some-file.txt");
|
|
|
|
let name = Name::new(&path, FileType::File);
|
|
|
|
assert_eq!(Some(String::from("txt")), name.extension());
|
|
}
|
|
|
|
#[test]
|
|
fn test_extensions_with_file_without_extension() {
|
|
let path = Path::new(".gitignore");
|
|
|
|
let name = Name::new(&path, FileType::File);
|
|
|
|
assert_eq!(None, name.extension());
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_hidder_with_hidden_file() {
|
|
let path = Path::new(".gitignore");
|
|
|
|
let name = Name::new(&path, FileType::File);
|
|
|
|
assert_eq!(true, name.is_hidden());
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_hidder_with_visible_file() {
|
|
let path = Path::new("some-file.txt");
|
|
|
|
let name = Name::new(&path, FileType::File);
|
|
|
|
assert_eq!(false, name.is_hidden());
|
|
}
|
|
|
|
#[test]
|
|
fn test_order_impl_is_case_insensitive() {
|
|
let path_1 = Path::new("AAAA");
|
|
let name_1 = Name::new(&path_1, FileType::File);
|
|
|
|
let path_2 = Path::new("aaaa");
|
|
let name_2 = Name::new(&path_2, FileType::File);
|
|
|
|
assert_eq!(Ordering::Equal, name_1.cmp(&name_2));
|
|
}
|
|
|
|
#[test]
|
|
fn test_partial_order_impl() {
|
|
let path_a = Path::new("aaaa");
|
|
let name_a = Name::new(&path_a, FileType::File);
|
|
|
|
let path_z = Path::new("zzzz");
|
|
let name_z = Name::new(&path_z, FileType::File);
|
|
|
|
assert_eq!(true, name_a < name_z);
|
|
}
|
|
|
|
#[test]
|
|
fn test_partial_order_impl_is_case_insensitive() {
|
|
let path_a = Path::new("aaaa");
|
|
let name_a = Name::new(&path_a, FileType::File);
|
|
|
|
let path_z = Path::new("ZZZZ");
|
|
let name_z = Name::new(&path_z, FileType::File);
|
|
|
|
assert_eq!(true, name_a < name_z);
|
|
}
|
|
|
|
#[test]
|
|
fn test_partial_eq_impl() {
|
|
let path_1 = Path::new("aaaa");
|
|
let name_1 = Name::new(&path_1, FileType::File);
|
|
|
|
let path_2 = Path::new("aaaa");
|
|
let name_2 = Name::new(&path_2, FileType::File);
|
|
|
|
assert_eq!(true, name_1 == name_2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_partial_eq_impl_is_case_insensitive() {
|
|
let path_1 = Path::new("AAAA");
|
|
let name_1 = Name::new(&path_1, FileType::File);
|
|
|
|
let path_2 = Path::new("aaaa");
|
|
let name_2 = Name::new(&path_2, FileType::File);
|
|
|
|
assert_eq!(true, name_1 == name_2);
|
|
}
|
|
}
|