mirror of
https://github.com/lsd-rs/lsd
synced 2024-12-13 21:52:37 +00:00
color: ✨ add parse theme file
Signed-off-by: zwPapEr <zw.paper@gmail.com>
This commit is contained in:
parent
ec77d91952
commit
b553d07faa
8 changed files with 374 additions and 123 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -24,6 +24,7 @@ version = "0.12.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ clap = "2.33.*"
|
|||
version_check = "0.9.*"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.*"
|
||||
ansi_term = { version = "0.12.*", features = ["derive_serde_style"] }
|
||||
dirs = "3.0.*"
|
||||
libc = "0.2.*"
|
||||
human-sort = "0.2.2"
|
||||
|
|
170
src/color.rs
170
src/color.rs
|
@ -1,6 +1,11 @@
|
|||
mod theme;
|
||||
|
||||
use theme::Theme;
|
||||
|
||||
use crate::flags::color::ThemeOption;
|
||||
|
||||
use ansi_term::{ANSIString, Colour, Style};
|
||||
use lscolors::{Indicator, LsColors};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -60,37 +65,81 @@ impl Elem {
|
|||
pub fn has_suid(&self) -> bool {
|
||||
matches!(self, Elem::Dir { uid: true } | Elem::File { uid: true, .. })
|
||||
}
|
||||
pub fn get_color(&self, theme: &theme::Theme) -> Colour {
|
||||
match self {
|
||||
Elem::File {
|
||||
exec: true,
|
||||
uid: true,
|
||||
} => theme.file_type.file.exec_uid,
|
||||
Elem::File {
|
||||
exec: false,
|
||||
uid: true,
|
||||
} => theme.file_type.file.uid_no_exec,
|
||||
Elem::File {
|
||||
exec: true,
|
||||
uid: false,
|
||||
} => theme.file_type.file.exec_no_uid,
|
||||
Elem::File {
|
||||
exec: false,
|
||||
uid: false,
|
||||
} => theme.file_type.file.no_exec_no_uid,
|
||||
Elem::SymLink => theme.file_type.symlink.default,
|
||||
Elem::BrokenSymLink => theme.file_type.symlink.broken,
|
||||
Elem::Dir { uid: true } => theme.file_type.dir.uid,
|
||||
Elem::Dir { uid: false } => theme.file_type.dir.no_uid,
|
||||
Elem::Pipe => theme.file_type.pipe,
|
||||
Elem::BlockDevice => theme.file_type.block_device,
|
||||
Elem::CharDevice => theme.file_type.char_device,
|
||||
Elem::Socket => theme.file_type.socket,
|
||||
Elem::Special => theme.file_type.special,
|
||||
|
||||
Elem::Read => theme.permissions.read,
|
||||
Elem::Write => theme.permissions.write,
|
||||
Elem::Exec => theme.permissions.exec,
|
||||
Elem::ExecSticky => theme.permissions.exec_sticky,
|
||||
Elem::NoAccess => theme.permissions.no_access,
|
||||
|
||||
Elem::DayOld => theme.modified.day_old,
|
||||
Elem::HourOld => theme.modified.hour_old,
|
||||
Elem::Older => theme.modified.older,
|
||||
|
||||
Elem::User => theme.user,
|
||||
Elem::Group => theme.group,
|
||||
|
||||
Elem::NonFile => theme.size.none,
|
||||
Elem::FileLarge => theme.size.large,
|
||||
Elem::FileMedium => theme.size.medium,
|
||||
Elem::FileSmall => theme.size.small,
|
||||
|
||||
Elem::INode { valid: false } => theme.inode.valid,
|
||||
Elem::INode { valid: true } => theme.inode.invalid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ColoredString<'a> = ANSIString<'a>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Theme {
|
||||
NoColor,
|
||||
Default,
|
||||
NoLscolors,
|
||||
}
|
||||
|
||||
pub struct Colors {
|
||||
colors: Option<HashMap<Elem, Colour>>,
|
||||
theme: Option<Theme>,
|
||||
lscolors: Option<LsColors>,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
pub fn new(theme: Theme) -> Self {
|
||||
let colors = match theme {
|
||||
Theme::NoColor => None,
|
||||
Theme::Default => Some(Self::get_light_theme_colour_map()),
|
||||
Theme::NoLscolors => Some(Self::get_light_theme_colour_map()),
|
||||
pub fn new(t: ThemeOption) -> Self {
|
||||
let theme = match t {
|
||||
ThemeOption::NoColor => None,
|
||||
ThemeOption::Default => Some(Theme::default_dark()),
|
||||
ThemeOption::NoLscolors => Some(Theme::default_dark()),
|
||||
ThemeOption::Custom(ref file) => {
|
||||
Some(Theme::from_path(file).unwrap_or_else(Theme::default_dark))
|
||||
}
|
||||
};
|
||||
let lscolors = match theme {
|
||||
Theme::NoColor => None,
|
||||
Theme::Default => Some(LsColors::from_env().unwrap_or_default()),
|
||||
Theme::NoLscolors => None,
|
||||
let lscolors = match t {
|
||||
ThemeOption::Default => Some(LsColors::from_env().unwrap_or_default()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
Self { colors, lscolors }
|
||||
Self { theme, lscolors }
|
||||
}
|
||||
|
||||
pub fn colorize<'a>(&self, input: String, elem: &Elem) -> ColoredString<'a> {
|
||||
|
@ -135,8 +184,8 @@ impl Colors {
|
|||
}
|
||||
|
||||
fn style_default(&self, elem: &Elem) -> Style {
|
||||
if let Some(ref colors) = self.colors {
|
||||
let style_fg = Style::default().fg(colors[elem]);
|
||||
if let Some(t) = &self.theme {
|
||||
let style_fg = Style::default().fg(elem.get_color(&t));
|
||||
if elem.has_suid() {
|
||||
style_fg.on(Colour::Fixed(124)) // Red3
|
||||
} else {
|
||||
|
@ -183,81 +232,4 @@ impl Colors {
|
|||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
// You can find the table for each color, code, and display at:
|
||||
//
|
||||
//https://jonasjacek.github.io/colors/
|
||||
fn get_light_theme_colour_map() -> HashMap<Elem, Colour> {
|
||||
let mut m = HashMap::new();
|
||||
// User / Group
|
||||
m.insert(Elem::User, Colour::Fixed(230)); // Cornsilk1
|
||||
m.insert(Elem::Group, Colour::Fixed(187)); // LightYellow3
|
||||
|
||||
// Permissions
|
||||
m.insert(Elem::Read, Colour::Green);
|
||||
m.insert(Elem::Write, Colour::Yellow);
|
||||
m.insert(Elem::Exec, Colour::Red);
|
||||
m.insert(Elem::ExecSticky, Colour::Purple);
|
||||
m.insert(Elem::NoAccess, Colour::Fixed(245)); // Grey
|
||||
|
||||
// File Types
|
||||
m.insert(
|
||||
Elem::File {
|
||||
exec: false,
|
||||
uid: false,
|
||||
},
|
||||
Colour::Fixed(184),
|
||||
); // Yellow3
|
||||
m.insert(
|
||||
Elem::File {
|
||||
exec: false,
|
||||
uid: true,
|
||||
},
|
||||
Colour::Fixed(184),
|
||||
); // Yellow3
|
||||
m.insert(
|
||||
Elem::File {
|
||||
exec: true,
|
||||
uid: false,
|
||||
},
|
||||
Colour::Fixed(40),
|
||||
); // Green3
|
||||
m.insert(
|
||||
Elem::File {
|
||||
exec: true,
|
||||
uid: true,
|
||||
},
|
||||
Colour::Fixed(40),
|
||||
); // Green3
|
||||
m.insert(Elem::Dir { uid: true }, Colour::Fixed(33)); // DodgerBlue1
|
||||
m.insert(Elem::Dir { uid: false }, Colour::Fixed(33)); // DodgerBlue1
|
||||
m.insert(Elem::Pipe, Colour::Fixed(44)); // DarkTurquoise
|
||||
m.insert(Elem::SymLink, Colour::Fixed(44)); // DarkTurquoise
|
||||
m.insert(Elem::BrokenSymLink, Colour::Fixed(124)); // Red3
|
||||
m.insert(Elem::BlockDevice, Colour::Fixed(44)); // DarkTurquoise
|
||||
m.insert(Elem::CharDevice, Colour::Fixed(172)); // Orange3
|
||||
m.insert(Elem::Socket, Colour::Fixed(44)); // DarkTurquoise
|
||||
m.insert(Elem::Special, Colour::Fixed(44)); // DarkTurquoise
|
||||
|
||||
// Last Time Modified
|
||||
m.insert(Elem::HourOld, Colour::Fixed(40)); // Green3
|
||||
m.insert(Elem::DayOld, Colour::Fixed(42)); // SpringGreen2
|
||||
m.insert(Elem::Older, Colour::Fixed(36)); // DarkCyan
|
||||
|
||||
// Last Time Modified
|
||||
m.insert(Elem::NonFile, Colour::Fixed(245)); // Grey
|
||||
m.insert(Elem::FileSmall, Colour::Fixed(229)); // Wheat1
|
||||
m.insert(Elem::FileMedium, Colour::Fixed(216)); // LightSalmon1
|
||||
m.insert(Elem::FileLarge, Colour::Fixed(172)); // Orange3
|
||||
|
||||
// INode
|
||||
m.insert(Elem::INode { valid: true }, Colour::Fixed(13)); // Pink
|
||||
m.insert(Elem::INode { valid: false }, Colour::Fixed(245)); // Grey
|
||||
m.insert(Elem::Links { valid: true }, Colour::Fixed(13));
|
||||
m.insert(Elem::Links { valid: false }, Colour::Fixed(245));
|
||||
|
||||
// TODO add this after we can use file to configure theme
|
||||
// m.insert(Elem::TreeEdge, Colour::Fixed(44)); // DarkTurquoise
|
||||
m
|
||||
}
|
||||
}
|
||||
|
|
188
src/color/theme.rs
Normal file
188
src/color/theme.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
///! This module provides methods to create theme from files and operations related to
|
||||
///! this.
|
||||
use crate::config_file;
|
||||
use crate::print_error;
|
||||
|
||||
use ansi_term::Colour;
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
/// A struct holding the theme configuration
|
||||
/// Color table: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.avg
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Theme {
|
||||
pub user: Colour,
|
||||
pub group: Colour,
|
||||
pub permissions: Permissions,
|
||||
pub file_type: FileType,
|
||||
pub modified: Modified,
|
||||
pub size: Size,
|
||||
pub inode: INode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Permissions {
|
||||
pub read: Colour,
|
||||
pub write: Colour,
|
||||
pub exec: Colour,
|
||||
pub exec_sticky: Colour,
|
||||
pub no_access: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct FileType {
|
||||
pub file: File,
|
||||
pub dir: Dir,
|
||||
pub pipe: Colour,
|
||||
pub symlink: Symlink,
|
||||
pub block_device: Colour,
|
||||
pub char_device: Colour,
|
||||
pub socket: Colour,
|
||||
pub special: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct File {
|
||||
pub exec_uid: Colour,
|
||||
pub uid_no_exec: Colour,
|
||||
pub exec_no_uid: Colour,
|
||||
pub no_exec_no_uid: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Dir {
|
||||
pub uid: Colour,
|
||||
pub no_uid: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Symlink {
|
||||
pub default: Colour,
|
||||
pub broken: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Modified {
|
||||
pub hour_old: Colour,
|
||||
pub day_old: Colour,
|
||||
pub older: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Size {
|
||||
pub none: Colour,
|
||||
pub small: Colour,
|
||||
pub medium: Colour,
|
||||
pub large: Colour,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct INode {
|
||||
pub valid: Colour,
|
||||
pub invalid: Colour,
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
/// This read theme from file,
|
||||
/// use the file path if it is absolute
|
||||
/// prefix the config_file dir to it if it is not
|
||||
pub fn from_path(file: &str) -> Option<Self> {
|
||||
let real = if let Some(path) = config_file::Config::expand_home(file) {
|
||||
path
|
||||
} else {
|
||||
print_error!("Bad theme file path: {}.", &file);
|
||||
return None;
|
||||
};
|
||||
let path = if Path::new(&real).is_absolute() {
|
||||
real
|
||||
} else {
|
||||
config_file::Config::config_file_path().unwrap().join(real)
|
||||
};
|
||||
match fs::read(&path) {
|
||||
Ok(f) => Self::with_yaml(&String::from_utf8_lossy(&f)),
|
||||
Err(e) => {
|
||||
print_error!("bad theme file: {}, {}\n", path.to_string_lossy(), e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This constructs a Theme struct with a passed [Yaml] str.
|
||||
fn with_yaml(yaml: &str) -> Option<Self> {
|
||||
match serde_yaml::from_str::<Self>(yaml) {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => {
|
||||
print_error!("theme file format error, {}\n\n", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn default_dark() -> Self {
|
||||
Theme {
|
||||
user: Colour::Fixed(230), // Cornsilk1
|
||||
group: Colour::Fixed(187), // LightYellow3
|
||||
permissions: Permissions {
|
||||
read: Colour::Green,
|
||||
write: Colour::Yellow,
|
||||
exec: Colour::Red,
|
||||
exec_sticky: Colour::Purple,
|
||||
no_access: Colour::Fixed(245), // Grey
|
||||
},
|
||||
file_type: FileType {
|
||||
file: File {
|
||||
exec_uid: Colour::Fixed(40), // Green3
|
||||
uid_no_exec: Colour::Fixed(184), // Yellow3
|
||||
exec_no_uid: Colour::Fixed(40), // Green3
|
||||
no_exec_no_uid: Colour::Fixed(184), // Yellow3
|
||||
},
|
||||
dir: Dir {
|
||||
uid: Colour::Fixed(33), // DodgerBlue1
|
||||
no_uid: Colour::Fixed(33), // DodgerBlue1
|
||||
},
|
||||
pipe: Colour::Fixed(44), // DarkTurquoise
|
||||
symlink: Symlink {
|
||||
default: Colour::Fixed(44), // DarkTurquoise
|
||||
broken: Colour::Fixed(124), // Red3
|
||||
},
|
||||
block_device: Colour::Fixed(44), // DarkTurquoise
|
||||
char_device: Colour::Fixed(172), // Orange3
|
||||
socket: Colour::Fixed(44), // DarkTurquoise
|
||||
special: Colour::Fixed(44), // DarkTurquoise
|
||||
},
|
||||
modified: Modified {
|
||||
hour_old: Colour::Fixed(40), // Green3
|
||||
day_old: Colour::Fixed(42), // SpringGreen2
|
||||
older: Colour::Fixed(36), // DarkCyan
|
||||
},
|
||||
size: Size {
|
||||
none: Colour::Fixed(245), // Grey
|
||||
small: Colour::Fixed(229), // Wheat1
|
||||
medium: Colour::Fixed(216), // LightSalmon1
|
||||
large: Colour::Fixed(172), // Orange3
|
||||
},
|
||||
inode: INode {
|
||||
valid: Colour::Fixed(13), // Pink
|
||||
invalid: Colour::Fixed(245), // Grey
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
///! This module provides methods to handle the program's config files and operations related to
|
||||
///! this.
|
||||
use crate::flags::color::ColorOption;
|
||||
use crate::flags::display::Display;
|
||||
use crate::flags::icons::{IconOption, IconTheme};
|
||||
use crate::flags::layout::Layout;
|
||||
use crate::flags::size::SizeFlag;
|
||||
use crate::flags::sorting::{DirGrouping, SortColumn};
|
||||
use crate::flags::{ColorOption, ThemeOption};
|
||||
///! This module provides methods to handle the program's config files and operations related to
|
||||
///! this.
|
||||
use crate::print_error;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -44,7 +44,8 @@ pub struct Config {
|
|||
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
pub struct Color {
|
||||
pub when: ColorOption,
|
||||
pub when: Option<ColorOption>,
|
||||
pub theme: Option<ThemeOption>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
|
@ -120,13 +121,11 @@ impl Config {
|
|||
/// This provides the path for a configuration file, according to the XDG_BASE_DIRS specification.
|
||||
/// return None if error like PermissionDenied
|
||||
#[cfg(not(windows))]
|
||||
fn config_file_path() -> Option<PathBuf> {
|
||||
pub fn config_file_path() -> Option<PathBuf> {
|
||||
use xdg::BaseDirectories;
|
||||
match BaseDirectories::with_prefix(CONF_DIR) {
|
||||
Ok(p) => {
|
||||
if let Ok(p) = p.place_config_file([CONF_FILE_NAME, YAML_LONG_EXT].join(".")) {
|
||||
return Some(p);
|
||||
}
|
||||
return Some(p.get_config_home());
|
||||
}
|
||||
Err(e) => print_error!("Can not open config file: {}.", e),
|
||||
}
|
||||
|
@ -136,22 +135,47 @@ impl Config {
|
|||
/// This provides the path for a configuration file, inside the %APPDATA% directory.
|
||||
/// return None if error like PermissionDenied
|
||||
#[cfg(windows)]
|
||||
fn config_file_path() -> Option<PathBuf> {
|
||||
pub fn config_file_path() -> Option<PathBuf> {
|
||||
if let Some(p) = dirs::config_dir() {
|
||||
return Some(
|
||||
p.join(CONF_DIR)
|
||||
.join(CONF_FILE_NAME)
|
||||
.with_extension(YAML_LONG_EXT),
|
||||
);
|
||||
return Some(p.join(CONF_DIR));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// This expand the `~` in path to HOME dir
|
||||
/// returns the origin one if no `~` found;
|
||||
/// returns None if error happened when getting home dir
|
||||
///
|
||||
/// Implementing this to reuse the `dirs` dependency, avoid adding new one
|
||||
pub fn expand_home<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
|
||||
let p = path.as_ref();
|
||||
if !p.starts_with("~") {
|
||||
return Some(p.to_path_buf());
|
||||
}
|
||||
if p == Path::new("~") {
|
||||
return dirs::home_dir();
|
||||
}
|
||||
dirs::home_dir().map(|mut h| {
|
||||
if h == Path::new("/") {
|
||||
// Corner case: `h` root directory;
|
||||
// don't prepend extra `/`, just drop the tilde.
|
||||
p.strip_prefix("~").unwrap().to_path_buf()
|
||||
} else {
|
||||
h.push(p.strip_prefix("~/").unwrap());
|
||||
h
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
if let Some(p) = Self::config_file_path() {
|
||||
if let Some(c) = Self::from_file(p.to_string_lossy().to_string()) {
|
||||
if let Some(c) = Self::from_file(
|
||||
p.join([CONF_FILE_NAME, YAML_LONG_EXT].join("."))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
|
10
src/core.rs
10
src/core.rs
|
@ -1,6 +1,8 @@
|
|||
use crate::color::{self, Colors};
|
||||
use crate::color::Colors;
|
||||
use crate::display;
|
||||
use crate::flags::{ColorOption, Display, Flags, IconOption, IconTheme, Layout, SortOrder};
|
||||
use crate::flags::{
|
||||
ColorOption, Display, Flags, IconOption, IconTheme, Layout, SortOrder, ThemeOption,
|
||||
};
|
||||
use crate::icon::{self, Icons};
|
||||
use crate::meta::Meta;
|
||||
use crate::{print_error, print_output, sort};
|
||||
|
@ -41,8 +43,8 @@ impl Core {
|
|||
let mut inner_flags = flags.clone();
|
||||
|
||||
let color_theme = match (tty_available && console_color_ok, flags.color.when) {
|
||||
(_, ColorOption::Never) | (false, ColorOption::Auto) => color::Theme::NoColor,
|
||||
_ => color::Theme::Default,
|
||||
(_, ColorOption::Never) | (false, ColorOption::Auto) => ThemeOption::NoColor,
|
||||
_ => flags.color.theme.clone(),
|
||||
};
|
||||
|
||||
let icon_theme = match (tty_available, flags.icons.when, flags.icons.theme) {
|
||||
|
|
|
@ -17,7 +17,7 @@ pub mod total_size;
|
|||
pub use blocks::Block;
|
||||
pub use blocks::Blocks;
|
||||
pub use color::Color;
|
||||
pub use color::ColorOption;
|
||||
pub use color::{ColorOption, ThemeOption};
|
||||
pub use date::DateFlag;
|
||||
pub use dereference::Dereference;
|
||||
pub use display::Display;
|
||||
|
|
|
@ -7,14 +7,17 @@ use crate::config_file::Config;
|
|||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::de::{self, Deserializer, Visitor};
|
||||
use serde::Deserialize;
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
|
||||
/// A collection of flags on how to use colors.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Color {
|
||||
/// When to use color.
|
||||
pub when: ColorOption,
|
||||
pub theme: ThemeOption,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
@ -23,7 +26,68 @@ impl Color {
|
|||
/// The [ColorOption] is configured with their respective [Configurable] implementation.
|
||||
pub fn configure_from(matches: &ArgMatches, config: &Config) -> Self {
|
||||
let when = ColorOption::configure_from(matches, config);
|
||||
Self { when }
|
||||
let theme = ThemeOption::from_config(config);
|
||||
Self { when, theme }
|
||||
}
|
||||
}
|
||||
|
||||
/// ThemeOption could be one of the following:
|
||||
/// Custom(*.yaml): use the YAML theme file as theme file
|
||||
/// if error happened, use the default theme
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum ThemeOption {
|
||||
NoColor,
|
||||
Default,
|
||||
NoLscolors,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl ThemeOption {
|
||||
fn from_config(config: &Config) -> ThemeOption {
|
||||
if let Some(c) = &config.color {
|
||||
if let Some(t) = &c.theme {
|
||||
return t.clone();
|
||||
}
|
||||
}
|
||||
|
||||
ThemeOption::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ThemeOption {
|
||||
fn default() -> Self {
|
||||
ThemeOption::Default
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for ThemeOption {
|
||||
fn deserialize<D>(deserializer: D) -> Result<ThemeOption, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct ThemeOptionVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ThemeOptionVisitor {
|
||||
type Value = ThemeOption;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("`no-color`, `default`, `no-lscolors` or <theme-file-path>")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<ThemeOption, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match value {
|
||||
"no-color" => Ok(ThemeOption::NoColor),
|
||||
"default" => Ok(ThemeOption::Default),
|
||||
"no-lscolors" => Ok(ThemeOption::NoLscolors),
|
||||
str => Ok(ThemeOption::Custom(str.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_identifier(ThemeOptionVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue