use colored::*;
use std::path::Path;
pub const FILE_COLOR: Color = Color::White;
pub const DIRECTORY_COLOR: Color = Color::BrightBlue;
pub const SYMLINK_COLOR: Color = Color::BrightCyan;
pub const EXECUTABLE_COLOR: Color = Color::BrightGreen;
pub const SIZE_COLOR: Color = Color::Green;
pub const DATE_COLOR: Color = Color::BrightBlue;
pub const USER_COLOR: Color = Color::Cyan;
pub const GROUP_COLOR: Color = Color::BrightBlack;
pub const PERMISSION_DIR_COLOR: Color = Color::BrightBlue;
pub const PERMISSION_READ_COLOR: Color = Color::BrightCyan;
pub const PERMISSION_WRITE_COLOR: Color = Color::BrightYellow;
pub const PERMISSION_EXEC_COLOR: Color = Color::BrightRed;
pub const PERMISSION_NONE_COLOR: Color = Color::BrightBlack;
pub const RUST_COLOR: Color = Color::Red;
pub const SCRIPT_COLOR: Color = Color::Yellow;
pub const TYPESCRIPT_COLOR: Color = Color::Blue;
pub const MARKUP_COLOR: Color = Color::Magenta;
pub const STYLE_COLOR: Color = Color::Cyan;
pub const DOC_COLOR: Color = Color::White;
pub const MEDIA_COLOR: Color = Color::Magenta;
pub const ARCHIVE_COLOR: Color = Color::Red;
pub const DATA_COLOR: Color = Color::Yellow;
fn get_extension_color(path: &Path) -> Option<Color> {
path.extension()
.and_then(|ext| ext.to_str())
.map(|ext| match ext.to_lowercase().as_str() {
"rs" | "toml" | "lock" => RUST_COLOR,
"js" | "mjs" | "cjs" | "jsx" | "json" | "json5" | "yaml" | "yml" => SCRIPT_COLOR,
"ts" | "tsx" | "d.ts" => TYPESCRIPT_COLOR,
"html" | "htm" | "xml" | "vue" | "ejs" => MARKUP_COLOR,
"css" | "scss" | "sass" | "less" | "conf" | "config" | "ini" | "env" => STYLE_COLOR,
"py" | "pyi" | "pyw" | "sh" | "bash" | "zsh" | "fish" | "bat" | "cmd" | "ps1" => {
EXECUTABLE_COLOR
}
"md" | "rst" | "txt" | "doc" | "docx" | "pdf" | "org" | "wiki" => DOC_COLOR,
"png" | "jpg" | "jpeg" | "gif" | "svg" | "bmp" | "ico" | "webp" | "tiff" | "mp4"
| "mov" | "avi" | "mkv" | "flv" | "wmv" | "webm" | "m4v" | "3gp" => MEDIA_COLOR,
"zip" | "tar" | "gz" | "rar" | "7z" | "iso" | "dmg" | "exe" | "dll" => ARCHIVE_COLOR,
"csv" | "tsv" | "sqlite" | "db" => DATA_COLOR,
_ => FILE_COLOR,
})
}
pub fn colorize_file_name(path: &Path) -> ColoredString {
let name = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_else(|| path.to_str().unwrap_or(""));
if path.is_dir() {
format!("{}/", name).color(DIRECTORY_COLOR).bold()
} else if path.is_symlink() {
name.color(SYMLINK_COLOR).italic().underline()
} else if is_executable(path) {
name.color(EXECUTABLE_COLOR).bold()
} else if let Some(color) = get_extension_color(path) {
name.color(color)
} else {
name.color(FILE_COLOR)
}
}
pub fn colorize_file_name_with_icon(path: &Path, content: String) -> ColoredString {
let parts: Vec<&str> = content.split(' ').collect();
if parts.len() != 2 {
return content.color(FILE_COLOR);
}
let icon = parts[0];
let name = parts[1];
if path.is_dir() {
format!("{} {}", icon, name.normal()).color(DIRECTORY_COLOR)
} else if path.is_symlink() {
format!("{} {}", icon, name.normal())
.color(SYMLINK_COLOR)
.italic()
.underline()
} else if is_executable(path) {
format!("{} {}", icon, name.normal()).color(EXECUTABLE_COLOR)
} else if let Some(color) = get_extension_color(path) {
format!("{} {}", icon, name.normal()).color(color)
} else {
format!("{} {}", icon, name.normal()).color(FILE_COLOR)
}
}
pub fn colorize_size(size: u64) -> ColoredString {
if size < 1024 {
format!("{}B", size).color(SIZE_COLOR)
} else if size < 1024 * 1024 {
format!("{:.1}K", size as f64 / 1024.0).color(SIZE_COLOR)
} else if size < 1024 * 1024 * 1024 {
format!("{:.1}M", size as f64 / (1024.0 * 1024.0)).color(SIZE_COLOR)
} else {
format!("{:.1}G", size as f64 / (1024.0 * 1024.0 * 1024.0)).color(SIZE_COLOR)
}
}
pub fn colorize_group(group: &str) -> ColoredString {
group.color(GROUP_COLOR)
}
pub fn colorize_user(user: &str) -> ColoredString {
user.color(USER_COLOR)
}
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
pub fn colorize_permissions(permissions: &Permissions) -> String {
let mode = permissions.mode();
let file_type = if mode & 0o40000 != 0 {
"d".color(PERMISSION_DIR_COLOR)
} else {
"-".color(PERMISSION_NONE_COLOR)
};
let user = triplet(mode, 6);
let group = triplet(mode, 3);
let other = triplet(mode, 0);
format!("{}{}{}{}", file_type, user, group, other)
}
fn triplet(mode: u32, shift: u32) -> String {
let r = if mode >> (shift + 2) & 1 != 0 {
"r".color(PERMISSION_READ_COLOR).to_string()
} else {
"-".color(PERMISSION_NONE_COLOR).to_string()
};
let w = if mode >> (shift + 1) & 1 != 0 {
"w".color(PERMISSION_WRITE_COLOR).to_string()
} else {
"-".color(PERMISSION_NONE_COLOR).to_string()
};
let x = if mode >> shift & 1 != 0 {
"x".color(PERMISSION_EXEC_COLOR).to_string()
} else {
"-".color(PERMISSION_NONE_COLOR).to_string()
};
format!("{}{}{}", r, w, x)
}
pub fn colorize_date(date: &std::time::SystemTime) -> ColoredString {
let datetime: chrono::DateTime<chrono::Local> = (*date).into();
datetime.format("%b %d %H:%M").to_string().color(DATE_COLOR)
}
fn is_executable(path: &Path) -> bool {
#[cfg(unix)]
{
if let Ok(metadata) = path.metadata() {
return metadata.permissions().mode() & 0o111 != 0;
}
}
false
}