Support Multi-file configuration (#45)

* install pango in CI

* <includes> xml support and EwwConfig merging

* Moved EwwConfig changes after merge

* Implemented relative paths

* Implemented paths in error messages

* Corrected include's error context message

* Written merge_includes test, fixed a simple bug

* Added "includes" label error check

* cargo format

* install pango in CI

* <includes> xml support and EwwConfig merging

* Moved EwwConfig changes after merge

* Implemented relative paths

* Implemented paths in error messages

* Corrected include's error context message

* Written merge_includes test, fixed a simple bug

* Added "includes" label error check

* cargo format

* Removed outdated comments and unused imports

* Fix dependency error

* Updated docs to add the <includes> block

* Removed unnecessary comment

Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com>

* Removed duplicated test block

Should'nt be there in the first place. My bad.

Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com>

* Better error context handling in eww_config.rs

Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com>

* Better error context handling in eww_config.rs (again)

Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com>

* Removed unnecessary error check

Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com>

Co-authored-by: elkowar <5300871+elkowar@users.noreply.github.com>
Co-authored-by: druskus20@gmail.com <druskus20>
This commit is contained in:
Pedro Burgos 2020-11-20 20:09:46 +01:00 committed by GitHub
parent ac8bc7d8b5
commit bc555900d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 9 deletions

View File

@ -25,6 +25,10 @@ $HOME
Your config structure should look like this:
```xml
<eww>
<definitions>
<!-- Put your <file>'s in here -->
</definitions>
<definitions>
<!-- Put your <def>'s in here -->
</definitions>
@ -38,7 +42,9 @@ Your config structure should look like this:
</windows>
</eww>
```
See [The `<definitons>` block](#the-definitions-block),
See
[The `<includes>` block](#the-includes-block),
[The `<definitons>` block](#the-definitions-block),
[Variables](#variables) and the
[The `<windows>` 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
</script-var>
</variables>
```
### The `<includes>` 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
<includes>
<file path="./something.xml"/>
<file path="./somethingelse.xml"/>
</includes>
```
### The `<definitions>` 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.

View File

@ -23,15 +23,40 @@ pub struct EwwConfig {
}
impl EwwConfig {
pub fn merge_includes(eww_config: EwwConfig, includes: Vec<EwwConfig>) -> Result<EwwConfig> {
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<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
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<Self> {
pub fn from_xml_element<P: AsRef<std::path::Path>>(xml: XmlElement, path: P) -> Result<Self> {
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::<Result<Vec<_>>>()
.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::<Result<HashMap<_, _>>>()
.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::<Result<HashMap<_, _>>>()
.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#"
<eww>
<definitions>
<def name="test1">
<box orientation="v">
{{var1}}
</box>
</def>
</definitions>
<variables>
<var name="var1">var1</var>
</variables>
<windows>
<window name="window1">
<size x="100" y="200" />
<pos x="100" y="200" />
<widget>
<test1 name="test2" />
</widget>
</window>
</windows>
</eww>
"#;
let input2 = r#"
<eww>
<definitions>
<def name="test2">
<box orientation="v">
{{var2}}
</box>
</def>
</definitions>
<variables>
<var name="var2">var2</var>
</variables>
<windows>
<window name="window2">
<size x="100" y="200" />
<pos x="100" y="200" />
<widget>
<test2 name="test2" />
</widget>
</window>
</windows>
</eww>
"#;
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);
}
}

View File

@ -2,6 +2,7 @@ use crate::{
util,
value::{PrimitiveValue, VarName},
};
use anyhow::*;
use element::*;

View File

@ -11,7 +11,6 @@ use crate::{
use anyhow::*;
use app::EwwCommand;
use itertools::Itertools;
use dashmap::DashMap;
use std::io::BufRead;