| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | use crate::modules::clock::ClockModule;
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:40:11 +01:00
										 |  |  | use crate::modules::focused::FocusedModule;
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | use crate::modules::launcher::LauncherModule;
 | 
					
						
							|  |  |  | use crate::modules::mpd::MpdModule;
 | 
					
						
							|  |  |  | use crate::modules::script::ScriptModule;
 | 
					
						
							|  |  |  | use crate::modules::sysinfo::SysInfoModule;
 | 
					
						
							|  |  |  | use crate::modules::tray::TrayModule;
 | 
					
						
							|  |  |  | use crate::modules::workspaces::WorkspacesModule;
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  | use color_eyre::eyre::{Context, ContextCompat};
 | 
					
						
							|  |  |  | use color_eyre::{eyre, Help, Report};
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | use dirs::config_dir;
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  | use eyre::Result;
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | use serde::Deserialize;
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  | use std::collections::HashMap;
 | 
					
						
							|  |  |  | use std::path::{Path, PathBuf};
 | 
					
						
							|  |  |  | use std::{env, fs};
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #[derive(Debug, Deserialize, Clone)]
 | 
					
						
							|  |  |  | #[serde(tag = "type", rename_all = "kebab-case")]
 | 
					
						
							|  |  |  | pub enum ModuleConfig {
 | 
					
						
							|  |  |  |     Clock(ClockModule),
 | 
					
						
							|  |  |  |     Mpd(MpdModule),
 | 
					
						
							|  |  |  |     Tray(TrayModule),
 | 
					
						
							|  |  |  |     Workspaces(WorkspacesModule),
 | 
					
						
							|  |  |  |     SysInfo(SysInfoModule),
 | 
					
						
							|  |  |  |     Launcher(LauncherModule),
 | 
					
						
							|  |  |  |     Script(ScriptModule),
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:40:11 +01:00
										 |  |  |     Focused(FocusedModule),
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  | #[derive(Debug, Deserialize, Clone)]
 | 
					
						
							|  |  |  | #[serde(untagged)]
 | 
					
						
							|  |  |  | pub enum MonitorConfig {
 | 
					
						
							|  |  |  |     Single(Config),
 | 
					
						
							|  |  |  |     Multiple(Vec<Config>),
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:40:11 +01:00
										 |  |  | #[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  | #[serde(rename_all = "kebab-case")]
 | 
					
						
							|  |  |  | pub enum BarPosition {
 | 
					
						
							|  |  |  |     Top,
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:40:11 +01:00
										 |  |  |     Bottom,
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | impl Default for BarPosition {
 | 
					
						
							|  |  |  |     fn default() -> Self {
 | 
					
						
							| 
									
										
										
										
											2022-08-15 21:11:17 +01:00
										 |  |  |         Self::Bottom
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | #[derive(Debug, Deserialize, Clone, Default)]
 | 
					
						
							|  |  |  | pub struct Config {
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  |     #[serde(default = "default_bar_position")]
 | 
					
						
							|  |  |  |     pub position: BarPosition,
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:41:38 +01:00
										 |  |  |     #[serde(default = "default_bar_height")]
 | 
					
						
							|  |  |  |     pub height: i32,
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  |     pub left: Option<Vec<ModuleConfig>>,
 | 
					
						
							|  |  |  |     pub center: Option<Vec<ModuleConfig>>,
 | 
					
						
							|  |  |  |     pub right: Option<Vec<ModuleConfig>>,
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  |     pub monitors: Option<HashMap<String, MonitorConfig>>,
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 15:56:21 +01:00
										 |  |  | const fn default_bar_position() -> BarPosition {
 | 
					
						
							|  |  |  |     BarPosition::Bottom
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:41:38 +01:00
										 |  |  | const fn default_bar_height() -> i32 {
 | 
					
						
							|  |  |  |     42
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | impl Config {
 | 
					
						
							| 
									
										
										
										
											2022-08-28 16:57:41 +01:00
										 |  |  |     /// Attempts to load the config file from file,
 | 
					
						
							|  |  |  |     /// parse it and return a new instance of `Self`.
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |     pub fn load() -> Result<Self> {
 | 
					
						
							|  |  |  |         let config_path = if let Ok(config_path) = env::var("IRONBAR_CONFIG") {
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  |             let path = PathBuf::from(config_path);
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |             if path.exists() {
 | 
					
						
							|  |  |  |                 Ok(path)
 | 
					
						
							|  |  |  |             } else {
 | 
					
						
							|  |  |  |                 Err(Report::msg("Specified config file does not exist")
 | 
					
						
							|  |  |  |                     .note("Config file was specified using `IRONBAR_CONFIG` environment variable"))
 | 
					
						
							|  |  |  |             }
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  |         } else {
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |             Self::try_find_config()
 | 
					
						
							|  |  |  |         }?;
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |         Self::load_file(&config_path)
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 16:57:41 +01:00
										 |  |  |     /// Attempts to discover the location of the config file
 | 
					
						
							|  |  |  |     /// by checking each valid format's extension.
 | 
					
						
							|  |  |  |     ///
 | 
					
						
							|  |  |  |     /// Returns the path of the first valid match, if any.
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |     fn try_find_config() -> Result<PathBuf> {
 | 
					
						
							|  |  |  |         let config_dir = config_dir().wrap_err("Failed to locate user config dir")?;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let extensions = vec!["json", "toml", "yaml", "yml", "corn"];
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |         let file = extensions.into_iter().find_map(|extension| {
 | 
					
						
							|  |  |  |             let full_path = config_dir
 | 
					
						
							|  |  |  |                 .join("ironbar")
 | 
					
						
							|  |  |  |                 .join(format!("config.{extension}"));
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |             if Path::exists(&full_path) {
 | 
					
						
							|  |  |  |                 Some(full_path)
 | 
					
						
							|  |  |  |             } else {
 | 
					
						
							|  |  |  |                 None
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         match file {
 | 
					
						
							|  |  |  |             Some(file) => Ok(file),
 | 
					
						
							|  |  |  |             None => Err(Report::msg("Could not find config file")),
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  |         }
 | 
					
						
							|  |  |  |     }
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-28 16:57:41 +01:00
										 |  |  |     /// Loads the config file at the specified path
 | 
					
						
							|  |  |  |     /// and parses it into `Self` based on its extension.
 | 
					
						
							| 
									
										
										
										
											2022-08-21 23:36:07 +01:00
										 |  |  |     fn load_file(path: &Path) -> Result<Self> {
 | 
					
						
							|  |  |  |         let file = fs::read(path).wrap_err("Failed to read config file")?;
 | 
					
						
							|  |  |  |         let extension = path
 | 
					
						
							|  |  |  |             .extension()
 | 
					
						
							|  |  |  |             .unwrap_or_default()
 | 
					
						
							|  |  |  |             .to_str()
 | 
					
						
							|  |  |  |             .unwrap_or_default();
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         match extension {
 | 
					
						
							|  |  |  |             "json" => serde_json::from_slice(&file).wrap_err("Invalid JSON config"),
 | 
					
						
							|  |  |  |             "toml" => toml::from_slice(&file).wrap_err("Invalid TOML config"),
 | 
					
						
							|  |  |  |             "yaml" | "yml" => serde_yaml::from_slice(&file).wrap_err("Invalid YAML config"),
 | 
					
						
							|  |  |  |             "corn" => {
 | 
					
						
							|  |  |  |                 // corn doesn't support deserialization yet
 | 
					
						
							|  |  |  |                 // so serialize the interpreted result then deserialize that
 | 
					
						
							|  |  |  |                 let file =
 | 
					
						
							|  |  |  |                     String::from_utf8(file).wrap_err("Config file contains invalid UTF-8")?;
 | 
					
						
							|  |  |  |                 let config = cornfig::parse(&file).wrap_err("Invalid corn config")?.value;
 | 
					
						
							|  |  |  |                 Ok(serde_json::from_str(&serde_json::to_string(&config)?)?)
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |             _ => unreachable!(),
 | 
					
						
							| 
									
										
										
										
											2022-08-15 17:06:42 +01:00
										 |  |  |         }
 | 
					
						
							| 
									
										
										
										
											2022-08-14 14:30:13 +01:00
										 |  |  |     }
 | 
					
						
							|  |  |  | }
 | 
					
						
							| 
									
										
										
										
											2022-08-14 20:40:11 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | pub const fn default_false() -> bool {
 | 
					
						
							|  |  |  |     false
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | pub const fn default_true() -> bool {
 | 
					
						
							|  |  |  |     true
 | 
					
						
							|  |  |  | }
 |