diff --git a/Cargo.toml b/Cargo.toml index f472f08..13c4f31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,15 @@ edition = "2018" lto = "fat" [features] -default = ["alsa-widget", "pulseaudio-widget"] +default = ["alsa-widget", "pulseaudio-widget", "yaml-cfg", "json-cfg"] damage_debug = [] + +yaml-cfg = ["serde_yaml"] +json-cfg = ["serde_json"] + alsa-widget = ["alsa"] pulseaudio-widget = ["libpulse-binding"] - [dependencies] smithay-client-toolkit = "0.6.4" byteorder = "1.3" @@ -30,9 +33,10 @@ lazy_static = "1.4" rcalc_lib = { version = "0.9" } rust-ini = "0.13" shlex = "0.1" -serde_json = "1.0" -serde_yaml = "0.8" + serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", optional = true } +serde_yaml = { version = "0.8", optional = true } libpulse-binding = { version = "2.11", optional = true } alsa = { version = "0.3.0", optional = true } diff --git a/src/configfmt.rs b/src/configfmt.rs new file mode 100644 index 0000000..4aa02ea --- /dev/null +++ b/src/configfmt.rs @@ -0,0 +1,61 @@ +use serde::{de::DeserializeOwned, Serialize}; +use std::io::BufRead; + +pub enum ConfigFmt { + #[cfg(feature = "yaml-cfg")] + Yaml, + #[cfg(feature = "json-cfg")] + Json, +} + +pub const CONFIG_NAMES: &'static [&'static str] = &[ + #[cfg(feature = "yaml-cfg")] + "config.yaml", + #[cfg(feature = "json-cfg")] + "config.json", +]; + +impl ConfigFmt { + #[inline] + pub fn new(ext: &str) -> Option { + match ext { + #[cfg(feature = "yaml-cfg")] + "yaml" => Some(ConfigFmt::Yaml), + #[cfg(feature = "json-cfg")] + "json" => Some(ConfigFmt::Json), + _ => None, + } + } + #[inline] + pub fn from_reader(&self, r: B) -> T { + match self { + #[cfg(feature = "yaml-cfg")] + ConfigFmt::Yaml => serde_yaml::from_reader(r).unwrap(), + #[cfg(feature = "json-cfg")] + ConfigFmt::Json => serde_json::from_reader(r).unwrap(), + } + } + #[inline] + pub fn to_string(&self, src: &T) -> String { + match self { + #[cfg(feature = "yaml-cfg")] + ConfigFmt::Yaml => serde_yaml::to_string(&src).unwrap(), + #[cfg(feature = "json-cfg")] + ConfigFmt::Json => serde_json::to_string_pretty(&src).unwrap(), + } + } +} + +impl Default for ConfigFmt { + fn default() -> Self { + // attributes require curly braces to work + #[cfg(feature = "yaml-cfg")] + { + ConfigFmt::Yaml + } + #[cfg(all(feature = "json-cfg", not(feature = "yaml-cfg")))] + { + ConfigFmt::Json + } + } +} diff --git a/src/main.rs b/src/main.rs index 23a4f97..e9e1aba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use std::default::Default; use std::env; use std::fs::File; use std::io::{BufRead, BufReader, Read, Write}; @@ -14,6 +13,7 @@ mod buffer; mod cmd; mod color; mod config; +mod configfmt; mod desktop; mod doublemempool; mod draw; @@ -22,13 +22,15 @@ mod widgets; use app::{App, OutputMode}; use cmd::Cmd; +use config::Config; +use configfmt::ConfigFmt; enum Mode { Start, Daemonize, StartOrKill, ToggleVisible, - PrintConfig(bool), + PrintConfig(ConfigFmt), } fn main() { @@ -44,40 +46,61 @@ fn main() { }, }; - let (is_yaml, config): (bool, config::Config) = match File::open(config_home.clone() + "/config.yaml") { - Ok(f) => { - let reader = BufReader::new(f); - (true, serde_yaml::from_reader(reader).unwrap()) - } - Err(_) => match File::open(config_home + "/config.json") { - Ok(f) => { - let reader = BufReader::new(f); - (false, serde_json::from_reader(reader).unwrap()) + // From all existing files take the first readable one and write it's extension to `ext` + let mut ext = [0x0; 8]; + let file = configfmt::CONFIG_NAMES + .iter() + .map(|name| std::path::Path::new(&config_home).join(name)) + .filter_map(|path| { + match File::open(&path) { + Ok(file) => { + let e = path.extension().and_then(|e| e.to_str())?; + let len = e.len(); + let from = len.saturating_sub(8); // the longest possible extension + ext[0..len - from].copy_from_slice(&e.as_bytes()[from..]); + Some(file) + } + Err(_) => None, } - Err(_) => (true, Default::default()), - } - }; + }) + .next(); + + let fmt = std::str::from_utf8(&ext) + .ok() + .and_then(ConfigFmt::new) + .unwrap_or_default(); + + let config: Config = file + .map(|f| fmt.from_reader(BufReader::new(f))) + .unwrap_or_default(); let scale = config.scale; let mut args = env::args(); let _ = args.next(); let mode = match args.next() { - Some(arg) => { - match arg.as_str() { - "start" => Mode::Daemonize, - "start-or-kill" => Mode::StartOrKill, - "toggle-visible" => Mode::ToggleVisible, - "print-config" => Mode::PrintConfig(!is_yaml), - "print-config-json" => Mode::PrintConfig(true), - "print-config-yaml" => Mode::PrintConfig(false), - s => { + Some(arg) => match arg.as_str() { + "start" => Mode::Daemonize, + "start-or-kill" => Mode::StartOrKill, + "toggle-visible" => Mode::ToggleVisible, + "print-config" => Mode::PrintConfig(fmt), + s => { + let p = "print-config-"; + let l = p.len(); + let fmt = if s.starts_with(p) { + ConfigFmt::new(&s[l..]) + } else { + None + }; + if let Some(fmt) = fmt { + Mode::PrintConfig(fmt) + } else { eprintln!("unsupported sub-command {}", s); std::process::exit(1); } } }, - None => Mode::Start + None => Mode::Start, }; if let Some(_) = args.next() { // total = args.count + 2 (the two we skipped + the rest) @@ -115,12 +138,8 @@ fn main() { }; daemon = true; } - Mode::PrintConfig(json) => { - if json { - println!("{}", serde_json::to_string_pretty(&config).unwrap()); - } else { - println!("{}", serde_yaml::to_string(&config).unwrap()); - } + Mode::PrintConfig(fmt) => { + println!("{}", fmt.to_string(&config)); std::process::exit(0); } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 5d37476..6494ff2 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -6,5 +6,5 @@ pub mod clock; pub mod date; pub mod launcher; -#[allow(dead_code)] +#[cfg(any(feature="alsa-widget", feature="pulseaudio-widget"))] pub mod audio;