diff --git a/ui/src/conf.rs b/ui/src/conf.rs index 71e33fdc..d8ae0cdd 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -289,14 +289,14 @@ pub struct Settings { impl FileSettings { pub fn new() -> Result { + let xdg_dirs = xdg::BaseDirectories::with_prefix("meli"); let config_path = match env::var("MELI_CONFIG") { Ok(path) => PathBuf::from(path), - Err(_) => { - let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap(); - xdg_dirs - .place_config_file("config") - .expect("cannot create configuration directory") - } + Err(_) => xdg_dirs + .as_ref() + .unwrap() + .place_config_file("config") + .expect("cannot create configuration directory"), }; if !config_path.exists() { println!( @@ -341,12 +341,54 @@ impl FileSettings { let Theme { light: default_light, dark: default_dark, + .. } = Theme::default(); for (k, v) in default_light.into_iter() { if !s.terminal.themes.light.contains_key(&k) { s.terminal.themes.light.insert(k, v); } } + for theme in s.terminal.themes.other_themes.values_mut() { + for (k, v) in default_dark.clone().into_iter() { + if !theme.contains_key(&k) { + theme.insert(k, v); + } + } + } + if let Ok(xdg_dirs) = xdg_dirs { + for theme_folder in xdg_dirs.find_config_files("themes") { + let read_dir = std::fs::read_dir(theme_folder)?; + for theme in read_dir { + let theme = theme?; + if theme.path().is_file() { + use std::os::unix::ffi::OsStrExt; + let theme_name = if let Some(n) = theme + .path() + .file_stem() + .map(|f| String::from_utf8_lossy(f.as_bytes()).into_owned()) + { + n + } else { + continue; + }; + let mut t: HashMap, ThemeAttributeInner> = + toml::from_str(&pp::pp(theme.path())?).map_err(|err| { + format!( + "Could not parse theme in `{}`: {}", + theme.path().display(), + err.to_string() + ) + })?; + for (k, v) in default_dark.clone().into_iter() { + if !t.contains_key(&k) { + t.insert(k, v); + } + } + s.terminal.themes.other_themes.insert(theme_name, t); + } + } + } + } for (k, v) in default_dark.into_iter() { if !s.terminal.themes.dark.contains_key(&k) { s.terminal.themes.dark.insert(k, v); @@ -359,7 +401,7 @@ impl FileSettings { pub fn validate(path: &str) -> Result<()> { let s = pp::pp(path)?; - let s: FileSettings = toml::from_str(&s).map_err(|e| { + let mut s: FileSettings = toml::from_str(&s).map_err(|e| { MeliError::new(format!( "{}:\nConfig file contains errors: {}", path, @@ -377,6 +419,44 @@ impl FileSettings { ); } } + + if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") { + for theme_folder in xdg_dirs.find_config_files("themes") { + let read_dir = std::fs::read_dir(theme_folder)?; + for theme in read_dir { + let theme = theme?; + if theme.path().is_file() { + use std::os::unix::ffi::OsStrExt; + let theme_name = if let Some(n) = theme + .path() + .file_stem() + .map(|f| String::from_utf8_lossy(f.as_bytes()).into_owned()) + { + n + } else { + continue; + }; + let t: HashMap, ThemeAttributeInner> = + toml::from_str(&pp::pp(theme.path())?).map_err(|err| { + format!( + "Could not parse theme in `{}`: {}", + theme.path().display(), + err.to_string() + ) + })?; + s.terminal.themes.other_themes.insert(theme_name, t); + } + } + } + } + match s.terminal.theme.as_str() { + "dark" | "light" => {} + t if s.terminal.themes.other_themes.contains_key(t) => {} + t => { + return Err(MeliError::new(format!("Theme `{}` was not found.", t))); + } + } + for (name, acc) in s.accounts { let FileAccount { root_folder, @@ -745,12 +825,11 @@ mod pp { } } - pub fn pp(path: &str) -> Result { - let p = &Path::new(path); - let p_buf: PathBuf = if p.is_relative() { - p.canonicalize()? + pub fn pp>(path: P) -> Result { + let p_buf: PathBuf = if path.as_ref().is_relative() { + path.as_ref().canonicalize()? } else { - p.to_path_buf() + path.as_ref().to_path_buf() }; let ret = pp_helper(&p_buf, 0); diff --git a/ui/src/conf/themes.rs b/ui/src/conf/themes.rs index eb66e808..8d2a3754 100644 --- a/ui/src/conf/themes.rs +++ b/ui/src/conf/themes.rs @@ -251,7 +251,7 @@ impl Serialize for ThemeValue { { match self { ThemeValue::Value(s) => s.serialize(serializer), - _ => unreachable!(), + ThemeValue::Link(s) => serializer.serialize_str(s.as_ref()), } } } @@ -560,14 +560,15 @@ impl Serialize for Theme { { let mut dark: HashMap, ThemeAttribute> = Default::default(); let mut light: HashMap, ThemeAttribute> = Default::default(); + let mut other_themes: HashMap = Default::default(); for k in self.dark.keys() { dark.insert( k.clone(), ThemeAttribute { - fg: unlink_fg(&self.light, k), - bg: unlink_bg(&self.light, k), - attrs: unlink_attrs(&self.light, k), + fg: unlink_fg(&self.dark, k), + bg: unlink_bg(&self.dark, k), + attrs: unlink_attrs(&self.dark, k), }, ); } @@ -583,16 +584,25 @@ impl Serialize for Theme { ); } - #[derive(Serialize)] - struct ThemeSer { - light: HashMap, ThemeAttribute>, - dark: HashMap, ThemeAttribute>, + for (name, t) in self.other_themes.iter() { + let mut new_map: HashMap, ThemeAttribute> = Default::default(); + + for k in t.keys() { + new_map.insert( + k.clone(), + ThemeAttribute { + fg: unlink_fg(&t, k), + bg: unlink_bg(&t, k), + attrs: unlink_attrs(&t, k), + }, + ); + } + other_themes.insert(name.to_string(), new_map); } - use serde::ser::SerializeStruct; - let mut s = serializer.serialize_struct("ThemeSer", 2)?; - s.serialize_field("light", &light)?; - s.serialize_field("dark", &dark)?; - s.end() + + other_themes.insert("light".to_string(), light); + other_themes.insert("dark".to_string(), dark); + other_themes.serialize(serializer) } } diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index b8291d71..9e6451bf 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -1098,8 +1098,8 @@ impl Serialize for Color { Color::Green | Color::Byte(2) => serializer.serialize_str("Green"), Color::Byte(3) => serializer.serialize_str("Olive"), Color::Byte(4) => serializer.serialize_str("Navy"), - Color::Byte(5) => serializer.serialize_str("Purple"), - Color::Byte(6) => serializer.serialize_str("Teal"), + Color::Byte(5) | Color::Magenta => serializer.serialize_str("Purple"), + Color::Byte(6) | Color::Cyan => serializer.serialize_str("Teal"), Color::Byte(7) => serializer.serialize_str("Silver"), Color::Byte(8) => serializer.serialize_str("Grey"), Color::Red | Color::Byte(9) => serializer.serialize_str("Red"), @@ -1349,7 +1349,10 @@ impl Serialize for Color { Color::Byte(253) => serializer.serialize_str("Grey85"), Color::Byte(254) => serializer.serialize_str("Grey89"), Color::Byte(255) => serializer.serialize_str("Grey93"), - _ => serializer.serialize_str("Black"), // FIXME + Color::Rgb(r, g, b) => { + serializer.serialize_str(&format!("#{:02x}{:02x}{:02x}", r, g, b)) + } + Color::Default => serializer.serialize_str("Default"), } } }