lsd/src/meta/name.rs
2018-12-17 11:43:28 +01:00

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);
}
}