Cleanup, improve error handling

This commit is contained in:
elkowar 2020-10-03 14:50:26 +02:00
parent 50b8840c5b
commit 8467cf3fde
8 changed files with 215 additions and 586 deletions

223
Cargo.lock generated
View file

@ -1,20 +1,5 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]] [[package]]
name = "ahash" name = "ahash"
version = "0.3.8" version = "0.3.8"
@ -88,20 +73,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "beef" name = "beef"
version = "0.4.4" version = "0.4.4"
@ -156,16 +127,6 @@ dependencies = [
"system-deps", "system-deps",
] ]
[[package]]
name = "calloop"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59561a8b3968ba4bda0c46f42e0568507c5d26e94c3b6f2a0c730cbecd83ff3a"
dependencies = [
"log",
"nix",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.60" version = "1.0.60"
@ -253,76 +214,11 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "encoding"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
dependencies = [
"encoding-index-japanese",
"encoding-index-korean",
"encoding-index-simpchinese",
"encoding-index-singlebyte",
"encoding-index-tradchinese",
]
[[package]]
name = "encoding-index-japanese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-korean"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-simpchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-singlebyte"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding-index-tradchinese"
version = "1.20141219.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18"
dependencies = [
"encoding_index_tests",
]
[[package]]
name = "encoding_index_tests"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]] [[package]]
name = "eww" name = "eww"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"calloop",
"crossbeam-channel", "crossbeam-channel",
"derive_more", "derive_more",
"extend", "extend",
@ -331,7 +227,6 @@ dependencies = [
"glib", "glib",
"grass", "grass",
"gtk", "gtk",
"hocon",
"hotwatch", "hotwatch",
"ipc-channel", "ipc-channel",
"itertools", "itertools",
@ -358,28 +253,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.12" version = "0.2.12"
@ -604,12 +477,6 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]] [[package]]
name = "gio" name = "gio"
version = "0.9.1" version = "0.9.1"
@ -800,20 +667,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hocon"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39554b1a6dc84511d3f8c0287ad63a78de60444b4032aec529d9e303ceaf0641"
dependencies = [
"failure",
"java-properties",
"memchr",
"nom",
"serde",
"uuid",
]
[[package]] [[package]]
name = "hotwatch" name = "hotwatch"
version = "0.4.3" version = "0.4.3"
@ -890,16 +743,6 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "java-properties"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4418ade5bde22a283a7f2fb537ea397ec102718f259f2630714e7a5b389fa"
dependencies = [
"encoding",
"regex",
]
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.2" version = "0.2.2"
@ -964,16 +807,6 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "miniz_oxide"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9"
dependencies = [
"adler",
"autocfg",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.6.22" version = "0.6.22"
@ -1028,28 +861,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "nix"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
dependencies = [
"bitflags",
"cc",
"cfg-if",
"libc",
]
[[package]]
name = "nom"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
dependencies = [
"memchr",
"version_check 0.1.5",
]
[[package]] [[package]]
name = "notify" name = "notify"
version = "4.0.15" version = "4.0.15"
@ -1144,12 +955,6 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.4.1" version = "1.4.1"
@ -1314,7 +1119,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
"version_check 0.9.2", "version_check",
] ]
[[package]] [[package]]
@ -1325,7 +1130,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"version_check 0.9.2", "version_check",
] ]
[[package]] [[package]]
@ -1451,12 +1256,6 @@ dependencies = [
"xmlparser", "xmlparser",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1569,18 +1368,6 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "synstructure"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]] [[package]]
name = "system-deps" name = "system-deps"
version = "1.3.2" version = "1.3.2"
@ -1719,12 +1506,6 @@ version = "0.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1"
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.2" version = "0.9.2"

View file

@ -13,7 +13,6 @@ gio = { version = "", features = ["v2_44"] }
glib = { version = "", features = ["v2_44"] } glib = { version = "", features = ["v2_44"] }
regex = "1" regex = "1"
hocon = { version = "0.3", default-features = false, features = [ "serde-support" ]}
try_match = "0.2.2" try_match = "0.2.2"
anyhow = "1.0" anyhow = "1.0"
derive_more = "0.99" derive_more = "0.99"
@ -24,7 +23,6 @@ serde = {version = "1.0", features = ["derive"]}
extend = "0.3.0" extend = "0.3.0"
grass = "0.10" grass = "0.10"
hotwatch = "0.4" hotwatch = "0.4"
calloop = "0.6"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
num = "0.3" num = "0.3"
stoppable_thread = "0.2" stoppable_thread = "0.2"

View file

@ -1,10 +1,9 @@
use super::*; use super::*;
use crate::value::AttrValue; use crate::value::AttrValue;
use hocon_ext::HoconExt; use crate::with_text_pos_context;
use maplit::hashmap; use maplit::hashmap;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct WidgetDefinition { pub struct WidgetDefinition {
@ -15,6 +14,7 @@ pub struct WidgetDefinition {
impl WidgetDefinition { impl WidgetDefinition {
pub fn from_xml_element(xml: XmlElement) -> Result<Self> { pub fn from_xml_element(xml: XmlElement) -> Result<Self> {
with_text_pos_context! { xml =>
if xml.tag_name() != "def" { if xml.tag_name() != "def" {
bail!( bail!(
"Illegal element: only <def> may be used in definition block, but found '{}'", "Illegal element: only <def> may be used in definition block, but found '{}'",
@ -22,36 +22,17 @@ impl WidgetDefinition {
); );
} }
let size: Option<Result<_>> = xml let size: Option<_> = try {
.child("size") (xml.attr("width").ok()?, xml.attr("height").ok()?)
.ok() };
.map(|node| Ok((node.attr("x")?.parse()?, node.attr("y")?.parse()?))); let size: Option<Result<_>> = size.map(|(x, y)| Ok((x.parse()?, y.parse()?)));
Ok(WidgetDefinition { WidgetDefinition {
name: xml.attr("name")?.to_owned(), name: xml.attr("name")?.to_owned(),
size: size.transpose()?, size: size.transpose()?,
structure: WidgetUse::from_xml_node(xml.only_child()?)?, structure: WidgetUse::from_xml_node(xml.only_child()?)?,
})
} }
}
pub fn parse_hocon(name: String, hocon: &Hocon) -> Result<Self> {
let definition = hocon.as_hash()?;
let structure = definition
.get("structure")
.cloned()
.context("structure must be set in widget definition")
.and_then(WidgetUse::parse_hocon)?;
Ok(WidgetDefinition {
name,
structure,
size: try {
(
definition.get("size_x")?.as_i64()? as i32,
definition.get("size_y")?.as_i64()? as i32,
)
},
})
} }
} }
@ -62,24 +43,6 @@ pub struct WidgetUse {
pub attrs: HashMap<String, AttrValue>, pub attrs: HashMap<String, AttrValue>,
} }
impl WidgetUse {
pub fn from_xml_node(xml: XmlNode) -> Result<Self> {
match xml {
XmlNode::Text(text) => Ok(WidgetUse::simple_text(AttrValue::parse_string(text.text()))),
XmlNode::Element(elem) => Ok(WidgetUse {
name: elem.tag_name().to_string(),
children: elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?,
attrs: elem
.attributes()
.iter()
.map(|attr| (attr.name().to_owned(), AttrValue::parse_string(attr.value().to_owned())))
.collect::<HashMap<_, _>>(),
}),
XmlNode::Ignored(_) => Err(anyhow!("Failed to parse node {:?} as widget use", xml)),
}
}
}
impl WidgetUse { impl WidgetUse {
pub fn new(name: String, children: Vec<WidgetUse>) -> Self { pub fn new(name: String, children: Vec<WidgetUse>) -> Self {
WidgetUse { WidgetUse {
@ -88,49 +51,21 @@ impl WidgetUse {
attrs: HashMap::new(), attrs: HashMap::new(),
} }
} }
pub fn from_xml_node(xml: XmlNode) -> Result<Self> {
pub fn parse_hocon(data: Hocon) -> Result<Self> { match xml {
match data { XmlNode::Text(text) => Ok(WidgetUse::simple_text(AttrValue::parse_string(text.text()))),
Hocon::Hash(data) => { XmlNode::Element(elem) => Ok(WidgetUse {
let (widget_name, widget_config) = data.into_iter().next().context("tried to parse empty hash as widget use")?; name: elem.tag_name().to_string(),
match widget_config { children: with_text_pos_context! { elem => elem.children().map(WidgetUse::from_xml_node).collect::<Result<_>>()?}?,
Hocon::Hash(widget_config) => WidgetUse::from_hash_definition(widget_name.clone(), widget_config), attrs: elem
direct_childen => Ok(WidgetUse::new( .attributes()
widget_name.clone(), .iter()
parse_widget_use_children(direct_childen)?, .map(|attr| (attr.name().to_owned(), AttrValue::parse_string(attr.value().to_owned())))
)), .collect::<HashMap<_, _>>(),
}),
XmlNode::Ignored(_) => Err(anyhow!("Failed to parse node {:?} as widget use", xml))?,
} }
} }
primitive => Ok(WidgetUse::simple_text(AttrValue::try_from(&primitive)?)),
}
}
/// generate a WidgetUse from an array-style definition
/// i.e.: { layout: [ "hi", "ho" ] }
pub fn from_array_definition(widget_name: String, children: Vec<Hocon>) -> Result<Self> {
let children = children.into_iter().map(WidgetUse::parse_hocon).collect::<Result<_>>()?;
Ok(WidgetUse::new(widget_name, children))
}
/// generate a WidgetUse from a hash-style definition
/// i.e.: { layout: { orientation: "v", children: ["hi", "Ho"] } }
pub fn from_hash_definition(widget_name: String, mut widget_config: HashMap<String, Hocon>) -> Result<Self> {
let children = widget_config
.remove("children")
.map(parse_widget_use_children)
.unwrap_or(Ok(Vec::new()))?;
let attrs = widget_config
.into_iter()
.filter_map(|(key, value)| Some((key.to_lowercase(), AttrValue::try_from(&value).ok()?)))
.collect();
Ok(WidgetUse {
name: widget_name.to_string(),
children,
attrs,
})
}
pub fn simple_text(text: AttrValue) -> Self { pub fn simple_text(text: AttrValue) -> Self {
WidgetUse { WidgetUse {
@ -147,94 +82,79 @@ impl WidgetUse {
} }
} }
pub fn parse_widget_use_children(children: Hocon) -> Result<Vec<WidgetUse>> { #[cfg(test)]
match children { mod test {
Hocon::Hash(_) => bail!( use super::*;
"children of a widget must either be a list of widgets or a primitive value, but got hash: {:?}", use maplit::hashmap;
children use pretty_assertions::assert_eq;
),
Hocon::Array(widget_children) => widget_children #[test]
.into_iter() fn test_simple_text() {
.map(WidgetUse::parse_hocon) let expected_attr_value = AttrValue::Concrete(PrimitiveValue::String("my text".to_owned()));
.collect::<Result<Vec<_>>>(), let widget = WidgetUse::simple_text(expected_attr_value.clone());
primitive => Ok(vec![WidgetUse::simple_text(AttrValue::try_from(&primitive)?)]), assert_eq!(
} widget,
WidgetUse {
name: "label".to_owned(),
children: Vec::new(),
attrs: hashmap! { "text".to_owned() => expected_attr_value},
},
)
} }
//#[cfg(test)] #[test]
//mod test { fn test_parse_widget_use() {
//use super::*; let input = r#"
//use maplit::hashmap; <widget_name attr1="hi" attr2="12">
//use pretty_assertions::assert_eq; <child_widget/>
foo
</widget_name>
"#;
let document = roxmltree::Document::parse(input).unwrap();
let xml = XmlNode::from(document.root_element().clone());
//#[test] println!("{}", xml);
//fn test_parse_widget_use() { assert_eq!(true, false);
//let input_complex = r#"{
//widget_name: {
//value: "test"
//children: [
//{ child: {} }
//{ child: { children: ["hi"] } }
//]
//}
//}"#;
//let expected = WidgetUse {
//name: "widget_name".to_string(),
//children: vec![
//WidgetUse::new("child".to_string(), vec![]),
//WidgetUse::new(
//"child".to_string(),
//vec![WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String(
//"hi".to_string(),
//)))],
//),
//],
//attrs: hashmap! { "value".to_string() => AttrValue::Concrete(PrimitiveValue::String("test".to_string()))},
//};
//assert_eq!(
//WidgetUse::parse_hocon(parse_hocon(input_complex).unwrap().clone()).unwrap(),
//expected
//);
//}
//#[test] let expected = WidgetUse {
//fn test_parse_widget_definition() { name: "widget_name".to_owned(),
//let input_complex = r#"{ attrs: hashmap! {
//structure: { foo: {} } "attr1".to_owned() => AttrValue::Concrete(PrimitiveValue::String("hi".to_owned())),
//}"#; "attr2".to_owned() => AttrValue::Concrete(PrimitiveValue::Number(12f64)),
//let expected = WidgetDefinition { },
//name: "widget_name".to_string(), children: vec![
//structure: WidgetUse::new("foo".to_string(), vec![]), WidgetUse::new("child_widget".to_owned(), Vec::new()),
//size: None, WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String("foo".to_owned()))),
//}; ],
//assert_eq!( };
//WidgetDefinition::parse_hocon("widget_name".to_string(), &parse_hocon(input_complex).unwrap()).unwrap(), assert_eq!(expected, WidgetUse::from_xml_node(xml).unwrap());
//expected }
//);
//}
//#[test] #[test]
//fn test_parse_widget_use_xml() { fn test_parse_widget_definition() {
//let input = r#" let input = r#"
//<widget_name attr1="hi" attr2="12"> <def name="foo" width="12" height="20">
//<child_widget/> <layout>test</layout>
//foo </def>
//</widget_name> "#;
//"#; let document = roxmltree::Document::parse(input).unwrap();
//let document = roxmltree::Document::parse(input).unwrap(); let xml = XmlNode::from(document.root_element().clone());
//let xml = document.root_element().clone();
//let expected = WidgetUse { let expected = WidgetDefinition {
//name: "widget_name".to_owned(), name: "foo".to_owned(),
//attrs: hashmap! { size: Some((12, 20)),
//"attr1".to_owned() => AttrValue::Concrete(PrimitiveValue::String("hi".to_owned())), structure: WidgetUse {
//"attr2".to_owned() => AttrValue::Concrete(PrimitiveValue::Number(12f64)), name: "layout".to_owned(),
//}, children: vec![WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String(
//children: vec![ "test".to_owned(),
//WidgetUse::new("child_widget".to_owned(), Vec::new()), )))],
//WidgetUse::simple_text(AttrValue::Concrete(PrimitiveValue::String("foo".to_owned()))), attrs: HashMap::new(),
//], },
//}; };
//assert_eq!(expected, WidgetUse::from_xml(xml).unwrap());
//} assert_eq!(
//} expected,
WidgetDefinition::from_xml_element(xml.as_element().unwrap()).unwrap()
);
}
}

View file

@ -1,23 +0,0 @@
use anyhow::*;
use hocon::Hocon;
use std::collections::HashMap;
pub trait HoconExt: Sized {
fn as_hash(&self) -> Result<&HashMap<String, Self>>;
fn as_array(&self) -> Result<&Vec<Self>>;
}
impl HoconExt for Hocon {
fn as_hash(&self) -> Result<&HashMap<String, Self>> {
match self {
Hocon::Hash(x) => Ok(x),
_ => Err(anyhow!("as_hash called with {:?}", self)),
}
}
fn as_array(&self) -> Result<&Vec<Self>> {
match self {
Hocon::Array(x) => Ok(x),
_ => Err(anyhow!("as_array called with {:?}", self)),
}
}
}

View file

@ -1,14 +1,10 @@
use crate::value::PrimitiveValue; use crate::value::PrimitiveValue;
use anyhow::*; use anyhow::*;
use element::*; use element::*;
use hocon::*;
use hocon_ext::HoconExt;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom;
use xml_ext::*; use xml_ext::*;
pub mod element; pub mod element;
pub mod hocon_ext;
pub mod xml_ext; pub mod xml_ext;
#[allow(unused)] #[allow(unused)]
@ -80,40 +76,6 @@ impl EwwConfig {
})) }))
} }
pub fn from_hocon(hocon: &Hocon) -> Result<Self> {
let data = hocon.as_hash()?;
let widgets = data
.get("widgets")
.context("widgets field missing")?
.as_hash()?
.iter()
.map(|(n, def)| Ok((n.clone(), WidgetDefinition::parse_hocon(n.clone(), def)?)))
.collect::<Result<_>>()?;
let windows = data
.get("windows")
.context("windows field missing")?
.as_hash()?
.iter()
.map(|(name, def)| Ok((name.clone(), EwwWindowDefinition::from_hocon(def)?)))
.collect::<Result<_>>()?;
let default_vars = data
.get("default_vars")
.unwrap_or(&Hocon::Hash(HashMap::new()))
.as_hash()?
.iter()
.map(|(name, def)| Ok((name.clone(), PrimitiveValue::try_from(def)?)))
.collect::<Result<_>>()?;
Ok(EwwConfig {
widgets,
windows,
default_vars,
})
}
pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> { pub fn get_widgets(&self) -> &HashMap<String, WidgetDefinition> {
&self.widgets &self.widgets
} }
@ -149,33 +111,4 @@ impl EwwWindowDefinition {
let widget = WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?; let widget = WidgetUse::from_xml_node(xml.child("widget")?.only_child()?)?;
Ok(EwwWindowDefinition { position, size, widget }) Ok(EwwWindowDefinition { position, size, widget })
} }
pub fn from_hocon(hocon: &Hocon) -> Result<Self> {
let data = hocon.as_hash().context("window config has to be a map structure")?;
let position: Option<_> = try {
(
data.get("pos")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
data.get("pos")?.as_hash().ok()?.get("y")?.as_i64()? as i32,
)
};
let size: Option<_> = try {
(
data.get("size")?.as_hash().ok()?.get("x")?.as_i64()? as i32,
data.get("size")?.as_hash().ok()?.get("y")?.as_i64()? as i32,
)
};
let element = WidgetUse::parse_hocon(data.get("widget").context("no widget use given")?.clone())?;
Ok(EwwWindowDefinition {
position: position.context("pos.x and pos.y need to be set")?,
size: size.context("size.x and size.y need to be set")?,
widget: element,
})
}
}
pub fn parse_hocon(s: &str) -> Result<Hocon> {
let s = s.trim();
Ok(HoconLoader::new().strict().load_str(s)?.hocon()?)
} }

View file

@ -1,24 +1,14 @@
use crate::util::StringExt; use crate::util::StringExt;
use anyhow::*; use anyhow::*;
use extend::ext;
use itertools::Itertools; use itertools::Itertools;
use std::fmt; use std::fmt;
#[ext(pub)] #[macro_export]
impl<'a, 'b> roxmltree::Node<'a, 'b> { macro_rules! with_text_pos_context {
fn find_child_with_tag(&self, tag_name: &str) -> Result<Self> ($node:expr => $($code:tt)*) => {{
where let result: Result<_> = try { $($code)* };
Self: Sized, result.with_context(|| anyhow!("at: {}", $node.text_pos()))
{ }};
self.children()
.find(|child| child.tag_name().name() == tag_name)
.with_context(|| anyhow!("node {} contained no child of type {}", self.tag_name().name(), tag_name,))
}
fn try_attribute(&self, key: &str) -> Result<&str> {
self.attribute(key)
.with_context(|| anyhow!("attribute '{}' missing from '{}'", key, self.tag_name().name()))
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -37,30 +27,6 @@ impl<'a, 'b> fmt::Display for XmlNode<'a, 'b> {
} }
} }
#[derive(Debug)]
pub struct XmlElement<'a, 'b>(roxmltree::Node<'a, 'b>);
impl<'a, 'b> fmt::Display for XmlElement<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let children = self
.children()
.map(|child| format!("{}", child))
.map(|x| x.lines().map(|line| format!(" {}", line)).join("\n"))
.join("\n");
write!(f, "{}{}</{}>", self.as_tag_string(), children, self.tag_name())
}
}
#[derive(Debug)]
pub struct XmlText<'a, 'b>(roxmltree::Node<'a, 'b>);
impl<'a, 'b> fmt::Display for XmlText<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Text({})", self.0.text().unwrap_or_default())
}
}
impl<'a, 'b> XmlNode<'a, 'b> { impl<'a, 'b> XmlNode<'a, 'b> {
pub fn as_text(self) -> Result<XmlText<'a, 'b>> { pub fn as_text(self) -> Result<XmlText<'a, 'b>> {
match self { match self {
@ -75,12 +41,60 @@ impl<'a, 'b> XmlNode<'a, 'b> {
_ => Err(anyhow!("'{}' is not an element node", self)), _ => Err(anyhow!("'{}' is not an element node", self)),
} }
} }
pub fn text_pos(&self) -> roxmltree::TextPos {
let document = self.node().document();
let range = self.node().range();
document.text_pos_at(range.start)
}
fn node(&self) -> roxmltree::Node<'a, 'b> {
match self {
XmlNode::Text(x) => x.0,
XmlNode::Element(x) => x.0,
XmlNode::Ignored(x) => x.clone(),
}
}
}
#[derive(Debug)]
pub struct XmlText<'a, 'b>(roxmltree::Node<'a, 'b>);
impl<'a, 'b> fmt::Display for XmlText<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Text(\"{}\")", self.text())
}
} }
impl<'a, 'b> XmlText<'a, 'b> { impl<'a, 'b> XmlText<'a, 'b> {
pub fn text(&self) -> String { pub fn text(&self) -> String {
self.0.text().unwrap_or_default().trim_lines().trim_matches('\n').to_owned() self.0.text().unwrap_or_default().trim_lines().trim_matches('\n').to_owned()
} }
pub fn text_pos(&self) -> roxmltree::TextPos {
let document = self.0.document();
let range = self.0.range();
document.text_pos_at(range.start)
}
}
#[derive(Debug)]
pub struct XmlElement<'a, 'b>(roxmltree::Node<'a, 'b>);
impl<'a, 'b> fmt::Display for XmlElement<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let children = self
.children()
.map(|child| format!("{}", child))
.map(|x| x.lines().map(|line| format!(" {}", line)).join("\n"))
.join("\n");
if children.len() == 0 {
write!(f, "{}</{}>", self.as_tag_string(), self.tag_name())
} else {
write!(f, "{}\n{}\n</{}>", self.as_tag_string(), children, self.tag_name())
}
}
} }
impl<'a, 'b> XmlElement<'a, 'b> { impl<'a, 'b> XmlElement<'a, 'b> {
@ -98,9 +112,11 @@ impl<'a, 'b> XmlElement<'a, 'b> {
} }
pub fn child(&self, tagname: &str) -> Result<XmlElement> { pub fn child(&self, tagname: &str) -> Result<XmlElement> {
with_text_pos_context! { self =>
self.child_elements() self.child_elements()
.find(|child| child.tag_name() == tagname) .find(|child| child.tag_name() == tagname)
.with_context(|| anyhow!("child element '{}' missing from {}", tagname, self.as_tag_string())) .with_context(|| anyhow!("child element '{}' missing from {}", tagname, self.as_tag_string()))?
}
} }
pub fn children(&self) -> impl Iterator<Item = XmlNode> { pub fn children(&self) -> impl Iterator<Item = XmlNode> {
@ -118,24 +134,36 @@ impl<'a, 'b> XmlElement<'a, 'b> {
} }
pub fn attr(&self, key: &str) -> Result<&str> { pub fn attr(&self, key: &str) -> Result<&str> {
with_text_pos_context! { self =>
self.0 self.0
.attribute(key) .attribute(key)
.with_context(|| anyhow!("'{}' missing attribute '{}'", self.as_tag_string(), key)) .with_context(|| anyhow!("'{}' missing attribute '{}'", self.as_tag_string(), key))?
}
} }
pub fn only_child(&self) -> Result<XmlNode> { pub fn only_child(&self) -> Result<XmlNode> {
with_text_pos_context! { self =>
let mut children_iter = self.children(); let mut children_iter = self.children();
let only_child = children_iter let only_child = children_iter
.next() .next()
.context(anyhow!("'{}' had no children", self.as_tag_string()))?; .with_context(|| anyhow!("'{}' had no children", self.as_tag_string()))?;
if children_iter.next().is_some() { if children_iter.next().is_some() {
bail!("'{}' had more than one child", &self); bail!("'{}' had more than one child", &self);
} }
Ok(only_child) only_child
}
} }
pub fn only_child_element(&self) -> Result<XmlElement> { pub fn only_child_element(&self) -> Result<XmlElement> {
Ok(self.only_child()?.as_element()?) with_text_pos_context! { self =>
self.only_child()?.as_element()?
}
}
pub fn text_pos(&self) -> roxmltree::TextPos {
let document = self.0.document();
let range = self.0.range();
document.text_pos_at(range.start)
} }
} }

View file

@ -1,22 +1,37 @@
use anyhow::*; use anyhow::*;
use derive_more; use derive_more;
use hocon::Hocon;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt;
use try_match::try_match; use try_match::try_match;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, derive_more::From)] #[derive(Clone, PartialEq, Deserialize, Serialize, derive_more::From)]
pub enum PrimitiveValue { pub enum PrimitiveValue {
String(String), String(String),
Number(f64), Number(f64),
Boolean(bool), Boolean(bool),
} }
impl fmt::Display for PrimitiveValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PrimitiveValue::String(s) => write!(f, "\"{}\"", s),
PrimitiveValue::Number(n) => write!(f, "{}", n),
PrimitiveValue::Boolean(b) => write!(f, "{}", b),
}
}
}
impl fmt::Debug for PrimitiveValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl std::str::FromStr for PrimitiveValue { impl std::str::FromStr for PrimitiveValue {
type Err = anyhow::Error; type Err = anyhow::Error;
/// parses the value, trying to turn it into a number and a boolean first, before deciding that it is a string. /// parses the value, trying to turn it into a number and a boolean first, before deciding that it is a string.
fn from_str(s: &str) -> Result<PrimitiveValue> { fn from_str(s: &str) -> Result<Self> {
Ok(PrimitiveValue::parse_string(s)) Ok(PrimitiveValue::parse_string(s))
} }
} }
@ -87,22 +102,6 @@ impl PrimitiveValue {
} }
} }
impl std::convert::TryFrom<&Hocon> for PrimitiveValue {
type Error = anyhow::Error;
fn try_from(value: &Hocon) -> Result<Self> {
Ok(match value {
Hocon::String(s) if s.starts_with("$$") => {
return Err(anyhow!("Tried to use variable reference {} as primitive value", s))
}
Hocon::String(s) => PrimitiveValue::String(s.to_string()),
Hocon::Integer(n) => PrimitiveValue::Number(*n as f64),
Hocon::Real(n) => PrimitiveValue::Number(*n as f64),
Hocon::Boolean(b) => PrimitiveValue::Boolean(*b),
_ => return Err(anyhow!("cannot convert {} to config::ConcreteValue")),
})
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AttrValue { pub enum AttrValue {
Concrete(PrimitiveValue), Concrete(PrimitiveValue),
@ -151,16 +150,3 @@ impl From<PrimitiveValue> for AttrValue {
AttrValue::Concrete(value) AttrValue::Concrete(value)
} }
} }
impl std::convert::TryFrom<&Hocon> for AttrValue {
type Error = anyhow::Error;
fn try_from(value: &Hocon) -> Result<Self> {
Ok(match value {
Hocon::String(s) => AttrValue::parse_string(s.clone()),
Hocon::Integer(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
Hocon::Real(n) => AttrValue::Concrete(PrimitiveValue::Number(*n as f64)),
Hocon::Boolean(b) => AttrValue::Concrete(PrimitiveValue::Boolean(*b)),
_ => return Err(anyhow!("cannot convert {:?} to config::AttrValue", &value)),
})
}
}

View file

@ -113,8 +113,14 @@ fn build_gtk_label(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
Ok(gtk_widget) Ok(gtk_widget)
} }
// TODO this is rather ugly,.....
fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> { fn build_gtk_text(bargs: &mut BuilderArgs) -> Result<gtk::Label> {
let text = bargs.widget.children.first().unwrap().get_attr("text")?; let text = bargs
.widget
.children
.first()
.context("text node must contain exactly one child")?
.get_attr("text")?;
let gtk_widget = gtk::Label::new(None); let gtk_widget = gtk::Label::new(None);
bargs.eww_state.resolve_str( bargs.eww_state.resolve_str(
bargs.local_env, bargs.local_env,