diff --git a/src/config.rs b/src/config.rs index 61ef060..102c252 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,9 @@ use crate::modules::tray::TrayModule; use crate::modules::workspaces::WorkspacesModule; use dirs::config_dir; use serde::Deserialize; -use std::fs; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::{env, fs}; #[derive(Debug, Deserialize, Clone)] #[serde(tag = "type", rename_all = "kebab-case")] @@ -23,6 +25,13 @@ pub enum ModuleConfig { Focused(FocusedModule), } +#[derive(Debug, Deserialize, Clone)] +#[serde(untagged)] +pub enum MonitorConfig { + Single(Config), + Multiple(Vec), +} + #[derive(Debug, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] pub enum BarPosition { @@ -47,7 +56,7 @@ pub struct Config { pub center: Option>, pub right: Option>, - pub monitors: Option>, + pub monitors: Option>, } const fn default_bar_position() -> BarPosition { @@ -60,35 +69,43 @@ const fn default_bar_height() -> i32 { impl Config { pub fn load() -> Option { - let config_dir = config_dir().expect("Failed to locate user config dir"); + if let Ok(config_path) = env::var("IRONBAR_CONFIG") { + let path = PathBuf::from(config_path); + Config::load_file(&path, path.extension().unwrap_or_default().to_str().unwrap_or_default()) + } else { + let config_dir = config_dir().expect("Failed to locate user config dir"); - let extensions = vec!["json", "toml", "yaml", "yml", "corn"]; + let extensions = vec!["json", "toml", "yaml", "yml", "corn"]; - extensions.into_iter().find_map(|extension| { - let full_path = config_dir - .join("ironbar") - .join(format!("config.{extension}")); + extensions.into_iter().find_map(|extension| { + let full_path = config_dir + .join("ironbar") + .join(format!("config.{extension}")); - if full_path.exists() { - let file = fs::read(full_path).expect("Failed to read config file"); - Some(match extension { - "json" => serde_json::from_slice(&file).expect("Invalid JSON config"), - "toml" => toml::from_slice(&file).expect("Invalid TOML config"), - "yaml" | "yml" => serde_yaml::from_slice(&file).expect("Invalid YAML config"), - "corn" => { - // corn doesn't support deserialization yet - // so serialize the interpreted result then deserialize that - let file = - String::from_utf8(file).expect("Config file contains invalid UTF-8"); - let config = cornfig::parse(&file).expect("Invalid corn config").value; - serde_json::from_str(&serde_json::to_string(&config).unwrap()).unwrap() - } - _ => unreachable!(), - }) - } else { - None - } - }) + Config::load_file(&full_path, extension) + }) + } + } + + fn load_file(path: &Path, extension: &str) -> Option { + if path.exists() { + let file = fs::read(path).expect("Failed to read config file"); + Some(match extension { + "json" => serde_json::from_slice(&file).expect("Invalid JSON config"), + "toml" => toml::from_slice(&file).expect("Invalid TOML config"), + "yaml" | "yml" => serde_yaml::from_slice(&file).expect("Invalid YAML config"), + "corn" => { + // corn doesn't support deserialization yet + // so serialize the interpreted result then deserialize that + let file = String::from_utf8(file).expect("Config file contains invalid UTF-8"); + let config = cornfig::parse(&file).expect("Invalid corn config").value; + serde_json::from_str(&serde_json::to_string(&config).unwrap()).unwrap() + } + _ => unreachable!(), + }) + } else { + None + } } } diff --git a/src/main.rs b/src/main.rs index e69c33e..cc25f72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ mod style; mod sway; use crate::bar::create_bar; -use crate::config::Config; +use crate::config::{Config, MonitorConfig}; use crate::style::load_css; use crate::sway::SwayOutput; use dirs::config_dir; @@ -40,6 +40,7 @@ async fn main() { let display = gdk::Display::default().expect("Failed to get default GDK display"); let num_monitors = display.n_monitors(); + for i in 0..num_monitors { let monitor = display.monitor(i).unwrap(); let monitor_name = &outputs @@ -47,11 +48,22 @@ async fn main() { .expect("GTK monitor output differs from Sway's") .name; - let config = config.monitors.as_ref().map_or(&config, |monitor_config| { - monitor_config.get(i as usize).unwrap_or(&config) - }); - - create_bar(app, &monitor, monitor_name, config.clone()); + if let Some(ref config) = config.monitors { + let config = config.get(monitor_name); + match &config { + Some(MonitorConfig::Single(config)) => { + create_bar(app, &monitor, monitor_name, config.clone()); + } + Some(MonitorConfig::Multiple(configs)) => { + for config in configs { + create_bar(app, &monitor, monitor_name, config.clone()); + } + } + _ => {} + } + } else { + create_bar(app, &monitor, monitor_name, config.clone()); + } } let style_path = config_dir()