wgui: add <macro> support, propagate variables into ParserResult
This commit is contained in:
@@ -20,8 +20,6 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
type VarMap = HashMap<Rc<str>, Rc<str>>;
|
||||
|
||||
#[self_referencing]
|
||||
struct XmlDocument {
|
||||
xml: String,
|
||||
@@ -44,6 +42,8 @@ struct ParserFile {
|
||||
|
||||
pub struct ParserResult {
|
||||
pub ids: HashMap<Rc<str>, WidgetID>,
|
||||
macro_attribs: HashMap<Rc<str>, MacroAttribs>,
|
||||
var_map: HashMap<Rc<str>, Rc<str>>,
|
||||
pub templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
pub path: PathBuf,
|
||||
}
|
||||
@@ -67,7 +67,13 @@ impl ParserResult {
|
||||
anyhow::bail!("no template named \"{}\" found", template_name);
|
||||
};
|
||||
|
||||
let mut ctx = create_default_context(layout);
|
||||
let mut ctx = ParserContext {
|
||||
layout,
|
||||
ids: Default::default(),
|
||||
macro_attribs: self.macro_attribs.clone(), // FIXME: prevent copying
|
||||
var_map: self.var_map.clone(), // FIXME: prevent copying
|
||||
templates: Default::default(),
|
||||
};
|
||||
|
||||
let file = ParserFile {
|
||||
document: template.node_document.clone(),
|
||||
@@ -75,21 +81,15 @@ impl ParserResult {
|
||||
template_parameters: template_parameters.clone(), // FIXME: prevent copying
|
||||
};
|
||||
|
||||
let node = template
|
||||
.node_document
|
||||
.borrow_doc()
|
||||
.get_node(template.node)
|
||||
.unwrap();
|
||||
|
||||
parse_widget_other_internal(
|
||||
template.clone(),
|
||||
template_parameters,
|
||||
&file,
|
||||
&mut ctx,
|
||||
node,
|
||||
widget_id,
|
||||
)?;
|
||||
|
||||
// FIXME?
|
||||
ctx.ids.into_iter().for_each(|(id, key)| {
|
||||
self.ids.insert(id, key);
|
||||
});
|
||||
@@ -98,11 +98,17 @@ impl ParserResult {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MacroAttribs {
|
||||
attribs: HashMap<Rc<str>, Rc<str>>,
|
||||
}
|
||||
|
||||
struct ParserContext<'a> {
|
||||
layout: &'a mut Layout,
|
||||
var_map: VarMap,
|
||||
templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
var_map: HashMap<Rc<str>, Rc<str>>,
|
||||
macro_attribs: HashMap<Rc<str>, MacroAttribs>,
|
||||
ids: HashMap<Rc<str>, WidgetID>,
|
||||
templates: HashMap<Rc<str>, Rc<Template>>,
|
||||
}
|
||||
|
||||
// Parses a color from a HTML hex string
|
||||
@@ -207,12 +213,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_widget_other_internal<'a>(
|
||||
fn parse_widget_other_internal(
|
||||
template: Rc<Template>,
|
||||
template_parameters: HashMap<Rc<str>, Rc<str>>,
|
||||
file: &'a ParserFile,
|
||||
file: &ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'a, 'a>,
|
||||
parent_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let template_file = ParserFile {
|
||||
@@ -245,16 +250,10 @@ fn parse_widget_other<'a>(
|
||||
return Ok(()); // not critical
|
||||
};
|
||||
|
||||
let template_parameters: HashMap<Rc<str>, Rc<str>> = iter_attribs(file, ctx, &node).collect();
|
||||
let template_parameters: HashMap<Rc<str>, Rc<str>> =
|
||||
iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
parse_widget_other_internal(
|
||||
template.clone(),
|
||||
template_parameters,
|
||||
file,
|
||||
ctx,
|
||||
node,
|
||||
parent_id,
|
||||
)
|
||||
parse_widget_other_internal(template.clone(), template_parameters, file, ctx, parent_id)
|
||||
}
|
||||
|
||||
fn parse_tag_include<'a>(
|
||||
@@ -333,7 +332,10 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
|
||||
|
||||
match vars.get(input_var) {
|
||||
Some(replacement) => replacement.clone(),
|
||||
None => Rc::from(""),
|
||||
None => {
|
||||
log::warn!("failed to replace var named \"{}\" (not found)", input_var);
|
||||
Rc::from("")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -341,31 +343,64 @@ pub fn replace_vars(input: &str, vars: &HashMap<Rc<str>, Rc<str>>) -> Rc<str> {
|
||||
}
|
||||
|
||||
#[allow(clippy::manual_strip)]
|
||||
fn process_attrib<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &'a ParserContext,
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> (Rc<str>, Rc<str>) {
|
||||
if value.starts_with("~") {
|
||||
let name = &value[1..];
|
||||
|
||||
(
|
||||
Rc::from(key),
|
||||
match ctx.var_map.get(name) {
|
||||
Some(name) => name.clone(),
|
||||
None => Rc::from("undefined"),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Rc::from(key),
|
||||
replace_vars(value, &file.template_parameters),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_attribs<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &'a ParserContext,
|
||||
node: &roxmltree::Node<'a, 'a>,
|
||||
node: &'a roxmltree::Node<'a, 'a>,
|
||||
is_tag_macro: bool,
|
||||
) -> impl Iterator<Item = (/*key*/ Rc<str>, /*value*/ Rc<str>)> + 'a {
|
||||
node.attributes().map(|attrib| {
|
||||
let mut res = Vec::<(Rc<str>, Rc<str>)>::new();
|
||||
|
||||
if is_tag_macro {
|
||||
// return as-is, no attrib post-processing
|
||||
for attrib in node.attributes() {
|
||||
let (key, value) = (attrib.name(), attrib.value());
|
||||
res.push((Rc::from(key), Rc::from(value)));
|
||||
}
|
||||
return res.into_iter();
|
||||
}
|
||||
|
||||
for attrib in node.attributes() {
|
||||
let (key, value) = (attrib.name(), attrib.value());
|
||||
|
||||
if value.starts_with("~") {
|
||||
let name = &value[1..];
|
||||
|
||||
(
|
||||
Rc::from(key),
|
||||
match ctx.var_map.get(name) {
|
||||
Some(name) => name.clone(),
|
||||
None => Rc::from("undefined"),
|
||||
},
|
||||
)
|
||||
if key == "macro" {
|
||||
if let Some(macro_attrib) = ctx.macro_attribs.get(value) {
|
||||
for (macro_key, macro_value) in macro_attrib.attribs.iter() {
|
||||
res.push(process_attrib(file, ctx, macro_key, macro_value));
|
||||
}
|
||||
} else {
|
||||
log::warn!("requested macro named \"{}\" not found!", value);
|
||||
}
|
||||
} else {
|
||||
(
|
||||
Rc::from(key),
|
||||
replace_vars(value, &file.template_parameters),
|
||||
)
|
||||
res.push(process_attrib(file, ctx, key, value));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
res.into_iter()
|
||||
}
|
||||
|
||||
fn parse_tag_theme<'a>(
|
||||
@@ -395,7 +430,7 @@ fn parse_tag_template(
|
||||
) -> anyhow::Result<()> {
|
||||
let mut template_name: Option<Rc<str>> = None;
|
||||
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match key.as_ref() {
|
||||
@@ -424,13 +459,51 @@ fn parse_tag_template(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_tag_macro(
|
||||
file: &ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'_, '_>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut macro_name: Option<Rc<str>> = None;
|
||||
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect();
|
||||
let mut macro_attribs = HashMap::<Rc<str>, Rc<str>>::new();
|
||||
|
||||
for (key, value) in attribs {
|
||||
match key.as_ref() {
|
||||
"name" => {
|
||||
macro_name = Some(value);
|
||||
}
|
||||
_ => {
|
||||
if macro_attribs.insert(key.clone(), value).is_some() {
|
||||
log::warn!("macro attrib \"{}\" already defined!", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let Some(name) = macro_name else {
|
||||
log::error!("Template name not specified, ignoring");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.macro_attribs.insert(
|
||||
name.clone(),
|
||||
MacroAttribs {
|
||||
attribs: macro_attribs,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_universal<'a>(
|
||||
file: &'a ParserFile,
|
||||
ctx: &mut ParserContext,
|
||||
node: roxmltree::Node<'a, 'a>,
|
||||
widget_id: WidgetID,
|
||||
) -> anyhow::Result<()> {
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node).collect();
|
||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||
|
||||
for (key, value) in attribs {
|
||||
#[allow(clippy::single_match)]
|
||||
@@ -485,6 +558,7 @@ fn create_default_context(layout: &mut Layout) -> ParserContext<'_> {
|
||||
ids: Default::default(),
|
||||
var_map: Default::default(),
|
||||
templates: Default::default(),
|
||||
macro_attribs: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,6 +578,8 @@ pub fn parse_from_assets(
|
||||
let result = ParserResult {
|
||||
ids: std::mem::take(&mut ctx.ids),
|
||||
templates: std::mem::take(&mut ctx.templates),
|
||||
macro_attribs: std::mem::take(&mut ctx.macro_attribs),
|
||||
var_map: std::mem::take(&mut ctx.var_map),
|
||||
path,
|
||||
};
|
||||
|
||||
@@ -571,6 +647,7 @@ fn parse_document_root(
|
||||
"include" => parse_tag_include(&file, ctx, child_node, parent_id)?,
|
||||
"theme" => parse_tag_theme(ctx, child_node)?,
|
||||
"template" => parse_tag_template(&file, ctx, child_node)?,
|
||||
"macro" => parse_tag_macro(&file, ctx, child_node)?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user