use crate::game::eu4::script::parser::{AstFile, AstStatement, AstValue};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WidgetNode {
pub kind: String,
pub name: String,
pub scripted: bool,
pub text: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WidgetPath {
pub gui_file: String,
pub ancestry: Vec<WidgetNode>,
}
impl WidgetPath {
pub fn leaf(&self) -> &WidgetNode {
self.ancestry
.last()
.expect("a widget path holds at least its widget")
}
}
pub fn named_widgets(gui_file: &str, ast: &AstFile) -> Vec<WidgetPath> {
let mut found = Vec::new();
let mut ancestry = Vec::new();
collect(gui_file, &ast.statements, &mut ancestry, &mut found);
found
}
fn collect(
gui_file: &str,
statements: &[AstStatement],
ancestry: &mut Vec<WidgetNode>,
found: &mut Vec<WidgetPath>,
) {
for statement in statements {
let AstStatement::Assignment {
key,
value: AstValue::Block { items, .. },
..
} = statement
else {
continue;
};
let name = scalar(items, "name");
if let Some(name) = &name {
ancestry.push(WidgetNode {
kind: key.clone(),
name: name.clone(),
scripted: scalar(items, "scripted").is_some_and(|value| value == "yes"),
text: scalar(items, "buttonText").filter(|text| !text.is_empty()),
});
found.push(WidgetPath {
gui_file: gui_file.to_owned(),
ancestry: ancestry.clone(),
});
}
collect(gui_file, items, ancestry, found);
if name.is_some() {
ancestry.pop();
}
}
}
pub(super) fn scalar(items: &[AstStatement], key: &str) -> Option<String> {
items.iter().find_map(|statement| match statement {
AstStatement::Assignment {
key: found,
value: AstValue::Scalar { value, .. },
..
} if found == key => Some(value.as_text()),
_ => None,
})
}