diff --git a/docs/content/main/configuration.md b/docs/content/main/configuration.md index 0907de1..943f33a 100644 --- a/docs/content/main/configuration.md +++ b/docs/content/main/configuration.md @@ -25,6 +25,10 @@ $HOME Your config structure should look like this: ```xml + + + + @@ -38,7 +42,9 @@ Your config structure should look like this: ``` -See [The `` block](#the-definitions-block), +See +[The `` block](#the-includes-block), +[The `` block](#the-definitions-block), [Variables](#variables) and the [The `` block](#the-windows-block). @@ -107,6 +113,15 @@ If you don't want a set interval and instead want it to tail (run the script whe ``` +### The `` block +Here you can include other config files so that they are merged together at startup. Currently namespaced variables are not supported so be careful when reusing code. + +```xml + + + + +``` ### The `` block In here you whole widget will be made, and you can also create your own widgets. Check [Widget Documentation](@/main/widgets.md) for pre-defined widgets. diff --git a/src/config/eww_config.rs b/src/config/eww_config.rs index 7f170b4..923e2ec 100644 --- a/src/config/eww_config.rs +++ b/src/config/eww_config.rs @@ -23,15 +23,40 @@ pub struct EwwConfig { } impl EwwConfig { + pub fn merge_includes(eww_config: EwwConfig, includes: Vec) -> Result { + let mut eww_config = eww_config.clone(); + for config in includes { + eww_config.widgets.extend(config.widgets); + eww_config.windows.extend(config.windows); + eww_config.script_vars.extend(config.script_vars); + eww_config.initial_variables.extend(config.initial_variables); + } + Ok(eww_config) + } + pub fn read_from_file>(path: P) -> Result { - let content = util::replace_env_var_references(std::fs::read_to_string(path)?); + let content = util::replace_env_var_references(std::fs::read_to_string(path.as_ref())?); let document = roxmltree::Document::parse(&content)?; - let result = EwwConfig::from_xml_element(XmlNode::from(document.root_element()).as_element()?.clone()); + let result = EwwConfig::from_xml_element(XmlNode::from(document.root_element()).as_element()?.clone(), path.as_ref()); result } - pub fn from_xml_element(xml: XmlElement) -> Result { + pub fn from_xml_element>(xml: XmlElement, path: P) -> Result { + let path = path.as_ref(); + let includes = match xml.child("includes") { + Ok(tag) => tag + .child_elements() + .map(|child| { + let childpath = child.attr("path")?; + let basepath = path.parent().unwrap(); + EwwConfig::read_from_file(basepath.join(childpath)) + }) + .collect::>>() + .context(format!("error handling include definitions: {}", path.display()))?, + Err(_) => Vec::new(), + }; + let definitions = xml .child("definitions")? .child_elements() @@ -40,7 +65,7 @@ impl EwwConfig { Ok((def.name.clone(), def)) }) .collect::>>() - .context("error parsing widget definitions")?; + .with_context(|| format!("error parsing widget definitions: {}", path.display()))?; let windows = xml .child("windows")? @@ -50,7 +75,7 @@ impl EwwConfig { Ok((def.name.to_owned(), def)) }) .collect::>>() - .context("error parsing window definitions")?; + .with_context(|| format!("error parsing window definitions: {}", path.display()))?; let variables_block = xml.child("variables").ok(); @@ -77,12 +102,13 @@ impl EwwConfig { } } - Ok(EwwConfig { + let current_config = EwwConfig { widgets: definitions, windows, initial_variables, script_vars, - }) + }; + EwwConfig::merge_includes(current_config, includes) } // TODO this is kinda ugly @@ -122,3 +148,78 @@ impl EwwConfig { self.script_vars.iter().find(|x| x.name() == name) } } + +#[cfg(test)] +mod test { + use crate::config::{EwwConfig, XmlNode}; + use std::collections::HashMap; + + #[test] + fn test_merge_includes() { + let input1 = r#" + + + + + {{var1}} + + + + + + var1 + + + + + + + + + + + + "#; + let input2 = r#" + + + + + {{var2}} + + + + + var2 + + + + + + + + + + + + "#; + + let document1 = roxmltree::Document::parse(&input1).unwrap(); + let document2 = roxmltree::Document::parse(input2).unwrap(); + let config1 = EwwConfig::from_xml_element(XmlNode::from(document1.root_element()).as_element().unwrap().clone(), ""); + let config2 = EwwConfig::from_xml_element(XmlNode::from(document2.root_element()).as_element().unwrap().clone(), ""); + let base_config = EwwConfig { + widgets: HashMap::new(), + windows: HashMap::new(), + initial_variables: HashMap::new(), + script_vars: Vec::new(), + }; + + let merged_config = EwwConfig::merge_includes(base_config, vec![config1.unwrap(), config2.unwrap()]).unwrap(); + + assert_eq!(merged_config.widgets.len(), 2); + assert_eq!(merged_config.windows.len(), 2); + assert_eq!(merged_config.initial_variables.len(), 2); + assert_eq!(merged_config.script_vars.len(), 0); + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 757c9d2..7654c48 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -2,6 +2,7 @@ use crate::{ util, value::{PrimitiveValue, VarName}, }; + use anyhow::*; use element::*; diff --git a/src/script_var_handler.rs b/src/script_var_handler.rs index f2da059..4412c8f 100644 --- a/src/script_var_handler.rs +++ b/src/script_var_handler.rs @@ -11,7 +11,6 @@ use crate::{ use anyhow::*; use app::EwwCommand; -use itertools::Itertools; use dashmap::DashMap; use std::io::BufRead;