use std::collections::HashMap;
use super::expr::Scope;
use crate::yui::{self, Element, err};
use crate::yui_resources::{Entry, Resources, local_name};
pub(super) struct Ctx {
pub(super) scope: Scope,
pub(super) fields: &'static str,
pub(super) tree: &'static str,
pub(super) owner: u16,
pub(super) slots: Vec<(String, String)>,
pub(super) update: String,
}
pub(super) const VIEW_FIELDS: &str = "self.";
pub(super) const INSTANCE_FIELDS: &str = "inst.";
pub(super) const VIEW_TREE: &str = "root";
pub(super) const ITEM_TREE: &str = "w";
pub(super) const SCREEN_OWNER: u16 = 0;
impl Ctx {
pub(super) fn new(scope: Scope, fields: &'static str, tree: &'static str, owner: u16) -> Self {
Ctx {
scope,
fields,
tree,
owner,
slots: Vec::new(),
update: String::new(),
}
}
}
pub(super) struct VmGen<'a> {
pub(super) file: String,
pub(super) mod_id: Option<&'a str>,
pub(super) theme: Option<yog_ui::theme::Theme>,
pub(super) resources: HashMap<String, String>,
pub(super) styles: HashMap<String, Vec<(String, String)>>,
pub(super) ids: yui::Names,
pub(super) view_fields: Vec<(String, String, String)>,
pub(super) parts: String,
pub(super) dispatch: Vec<(String, String)>,
pub(super) templates: String,
pub(super) n_slots: usize,
pub(super) n_lists: u16,
pub(super) n_templates: usize,
pub(super) n_scrollers: u16,
pub(super) n_text_boxes: u16,
pub(super) cmd_slots: HashMap<u16, u16>,
}
pub(super) fn path_lit(path: &[u16]) -> String {
format!(
"&[{}]",
path.iter()
.map(u16::to_string)
.collect::<Vec<_>>()
.join(", ")
)
}
impl<'a> VmGen<'a> {
pub(super) fn new(
file: String,
mod_id: Option<&'a str>,
theme: Option<yog_ui::theme::Theme>,
) -> Self {
VmGen {
file,
mod_id,
theme,
resources: HashMap::new(),
styles: HashMap::new(),
ids: yui::Names::default(),
view_fields: Vec::new(),
parts: String::new(),
dispatch: Vec::new(),
templates: String::new(),
n_slots: 0,
n_lists: 0,
n_templates: 0,
n_scrollers: 0,
n_text_boxes: 0,
cmd_slots: HashMap::new(),
}
}
pub(super) fn slot(&mut self, ctx: &mut Ctx, ty: &str) -> String {
let name = format!("b{}", self.n_slots);
self.n_slots += 1;
if ctx.scope == Scope::Template && ctx.fields == INSTANCE_FIELDS {
ctx.slots.push((name.clone(), ty.to_string()));
} else {
self.view_fields.push((
name.clone(),
format!("::yog_kit::bind::Slot<{ty}>"),
"::yog_kit::bind::Slot::new()".into(),
));
}
name
}
pub(super) fn next_cmd(&mut self, owner: u16) -> u16 {
let n = self.cmd_slots.entry(owner).or_insert(0);
let s = *n;
*n += 1;
s
}
fn resolved(&self, el: &Element, k: &str, v: &str) -> Result<String, String> {
let Some(name) = local_name(v) else {
return Ok(v.to_string());
};
match self.resources.get(name) {
Some(r) => Ok(r.clone()),
None if k == yog_ids::yui_attr::STYLE => Ok(v.to_string()),
None => err(
el,
format!("{k}=\"{v}\": no resource `{name}` in <Screen.Resources>"),
),
}
}
pub(super) fn resolve(&self, el: &Element) -> Result<Element, String> {
let mut out = el.clone();
out.attrs.clear();
let mut style: Vec<(String, String)> = Vec::new();
for (k, v) in &el.attrs {
let v = self.resolved(el, k, v)?;
match k.as_str() {
yog_ids::yui_attr::X_NAME => out.attrs.push((yog_ids::yui_attr::ID.into(), v)),
yog_ids::yui_attr::STYLE => {
let name = v.trim().trim_start_matches(yog_ids::sep::TYPED);
let Some(s) = self.styles.get(name) else {
return err(
el,
format!(
"style=\"{v}\": no <Style x:Key=\"{name}\"> in <Screen.Resources>"
),
);
};
style = s.clone();
}
_ => out.attrs.push((k.clone(), v)),
}
}
for (k, v) in style {
if !out.attrs.iter().any(|(ek, _)| *ek == k) {
out.attrs.push((k, v));
}
}
if let Some(theme) = &self.theme {
theme.apply(&el.name, &mut out.attrs);
}
Ok(out)
}
pub(super) fn collect_resources(&mut self, res: &Element) -> Result<(), String> {
let entries: Vec<Entry> = res
.children
.iter()
.map(|r| Entry {
kind: &r.name,
line: r.line,
attrs: r
.attrs
.iter()
.map(|(k, v)| (k.as_str(), v.as_str()))
.collect(),
})
.collect();
let read = Resources::read(&entries);
if let Some((line, msg)) = read.problems.into_iter().next() {
return Err(format!("line {line}: {msg}"));
}
self.resources = read.values;
self.styles = read.styles;
Ok(())
}
pub(super) fn absorb(&mut self, ids: Vec<String>) {
for id in &ids {
self.ids.push(id);
}
}
}