diff --git a/Cargo.lock b/Cargo.lock index 55381d7..fdb2182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,6 +310,7 @@ dependencies = [ "hotwatch", "ipc-channel", "itertools", + "lazy_static", "log 0.4.11", "maplit", "notify", diff --git a/Cargo.toml b/Cargo.toml index 004d22f..b248670 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ scheduled-executor = "0.4" debug_stub_derive = "0.3" log = "0.4" pretty_env_logger = "0.4" +lazy_static = "1.4.0" #thiserror = "1.0" diff --git a/src/config/element.rs b/src/config/element.rs index 6f730bd..547910f 100644 --- a/src/config/element.rs +++ b/src/config/element.rs @@ -1,4 +1,6 @@ use super::*; +use lazy_static::lazy_static; +use regex::Regex; use crate::value::AttrValue; use crate::with_text_pos_context; @@ -52,7 +54,13 @@ impl WidgetUse { } } pub fn from_xml_node(xml: XmlNode) -> Result { + lazy_static! { + static ref PATTERN: Regex = Regex::new("\\{\\{(.*)\\}\\}").unwrap(); + }; match xml { + // TODO the matching here is stupid. This currently uses the inefficient function to parse simple single varrefs, + // TODO and does the regex match twice in the from_text_with_var_refs part + XmlNode::Text(text) if PATTERN.is_match(&text.text()) => Ok(WidgetUse::from_text_with_var_refs(&text.text())), XmlNode::Text(text) => Ok(WidgetUse::simple_text(AttrValue::parse_string(text.text()))), XmlNode::Element(elem) => Ok(WidgetUse { name: elem.tag_name().to_string(), @@ -75,24 +83,19 @@ impl WidgetUse { } } - // TODO Even just thinking of this gives me horrible nightmares..... - //pub fn from_text(text: String) -> Self { - //WidgetUse::text_with_var_refs( - //text.split(" ") - //.map(|word| AttrValue::parse_string(word.to_owned())) - //.collect_vec(), - //) - //} - - pub fn text_with_var_refs(elements: Vec) -> Self { - dbg!(WidgetUse { + pub fn from_text_with_var_refs(text: &str) -> Self { + WidgetUse { name: "layout".to_owned(), attrs: hashmap! { - "halign".to_owned() => AttrValue::Concrete(PrimitiveValue::String("center".to_owned())), - "space-evenly".to_owned() => AttrValue::Concrete(PrimitiveValue::String("false".to_owned())), + "halign".to_owned() => AttrValue::Concrete(PrimitiveValue::String("center".to_owned())), + "space-evenly".to_owned() => AttrValue::Concrete(PrimitiveValue::String("false".to_owned())), }, - children: elements.into_iter().map(WidgetUse::simple_text).collect(), - }) + children: parse_string_with_var_refs(text) + .into_iter() + .map(StringOrVarRef::to_attr_value) + .map(WidgetUse::simple_text) + .collect(), + } } pub fn get_attr(&self, key: &str) -> Result<&AttrValue> { @@ -102,6 +105,62 @@ impl WidgetUse { } } +#[derive(Clone, Debug, PartialEq)] +enum StringOrVarRef { + String(String), + VarRef(String), +} + +impl StringOrVarRef { + fn to_attr_value(self) -> AttrValue { + match self { + StringOrVarRef::String(x) => AttrValue::Concrete(PrimitiveValue::parse_string(&x)), + StringOrVarRef::VarRef(x) => AttrValue::VarRef(x), + } + } +} + +// TODO this could be a fancy Iterator implementation, ig +fn parse_string_with_var_refs(s: &str) -> Vec { + let mut elements = Vec::new(); + + let mut cur_word = "".to_owned(); + let mut cur_varref: Option = None; + let mut curly_count = 0; + for c in s.chars() { + if let Some(ref mut varref) = cur_varref { + if c == '}' { + curly_count -= 1; + if curly_count == 0 { + elements.push(StringOrVarRef::VarRef(std::mem::take(varref))); + cur_varref = None + } + } else { + curly_count = 2; + varref.push(c); + } + } else { + if c == '{' { + curly_count += 1; + if curly_count == 2 { + if !cur_word.is_empty() { + elements.push(StringOrVarRef::String(std::mem::take(&mut cur_word))); + } + cur_varref = Some(String::new()) + } + } else { + cur_word.push(c); + } + } + } + if let Some(unfinished_varref) = cur_varref.take() { + elements.push(StringOrVarRef::String(unfinished_varref)); + } else if !cur_word.is_empty() { + elements.push(StringOrVarRef::String(cur_word.to_owned())); + } + elements +} + #[cfg(test)] mod test { use super::*; @@ -130,7 +189,7 @@ mod test { fn test_text_with_var_refs() { let expected_attr_value1 = mk_attr_str("my text"); let expected_attr_value2 = AttrValue::VarRef("var".to_owned()); - let widget = WidgetUse::text_with_var_refs(vec![expected_attr_value1.clone(), expected_attr_value2.clone()]); + let widget = WidgetUse::from_text_with_var_refs("my text{{var}}"); assert_eq!( widget, WidgetUse { @@ -199,4 +258,21 @@ mod test { WidgetDefinition::from_xml_element(xml.as_element().unwrap()).unwrap() ); } + + #[test] + fn test_parse_string_or_var_ref_list() { + let input = "{{foo}}{{bar}}baz{{bat}}quok{{test}}"; + let output = parse_string_with_var_refs(input); + assert_eq!( + output, + vec![ + StringOrVarRef::VarRef("foo".to_owned()), + StringOrVarRef::VarRef("bar".to_owned()), + StringOrVarRef::String("baz".to_owned()), + StringOrVarRef::VarRef("bat".to_owned()), + StringOrVarRef::String("quok".to_owned()), + StringOrVarRef::VarRef("test".to_owned()), + ], + ) + } } diff --git a/src/value.rs b/src/value.rs index 9adb14f..2b49f8a 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,7 +1,8 @@ use anyhow::*; use derive_more; +use lazy_static::lazy_static; +use regex::Regex; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::convert::TryFrom; use std::fmt; @@ -127,20 +128,18 @@ impl AttrValue { _ => Err(anyhow!("{:?} is not a bool", self)), } } - pub fn as_var_ref(&self) -> Result<&String> { - match self { - AttrValue::VarRef(x) => Ok(x), - _ => Err(anyhow!("{:?} is not a VarRef", self)), - } - } /// parses the value, trying to turn it into VarRef, /// a number and a boolean first, before deciding that it is a string. pub fn parse_string(s: String) -> Self { - if s.starts_with("$$") { - AttrValue::VarRef(s.trim_start_matches("$$").to_string()) + lazy_static! { + static ref PATTERN: Regex = Regex::new("^\\{\\{(.*)\\}\\}$").unwrap(); + }; + + if let Some(ref_name) = PATTERN.captures(&s).and_then(|cap| cap.get(1)).map(|x| x.as_str()) { + AttrValue::VarRef(ref_name.to_owned()) } else { - AttrValue::Concrete(PrimitiveValue::parse_string(&s)) + AttrValue::Concrete(PrimitiveValue::String(s)) } } } diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index e423484..33a89f8 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -70,7 +70,7 @@ pub fn build_gtk_widget( let child_widget = element_to_gtk_thing(widget_definitions, bargs.eww_state, local_env, child); let child_widget = child_widget.with_context(|| { format!( - "error while building child '{:?}' of '{}'", + "error while building child '{:#?}' of '{}'", &child, >k_widget.get_widget_name() )