1mod bind;
9mod binding;
10mod element;
11mod expr;
12mod list;
13mod state;
14mod target;
15
16use std::fmt::Write as _;
17
18pub(crate) use binding::compile_bind;
19
20use crate::rust_code::{f32_lit, fill};
21use crate::yui::{self, Element, UI_USE, err};
22use expr::Scope;
23use state::{Ctx, SCREEN_OWNER, VIEW_FIELDS, VIEW_TREE, VmGen};
24
25use yog_data::Diagnostic;
26use yog_ids::versions::UI_CODEGEN as CODEGEN;
28
29const NEW_VALUE: &str = "__v";
31const WIDGET: &str = "__w";
33
34const TEMPLATE: &str = include_str!("view.g.rs.in");
36
37struct Root {
39 id: String,
40 vm: String,
41 create: String,
42}
43
44fn read_root(root: &Element) -> Result<Root, Diagnostic> {
47 if root.name != yog_ids::yui::SCREEN {
48 return err(root, "a view-model screen's root must be <Screen>");
49 }
50 let id = root
51 .attr(yog_ids::yui_attr::ID)
52 .filter(|s| !s.trim().is_empty())
53 .ok_or_else(|| {
54 Diagnostic::from("<Screen> needs an id (the UI id, e.g. \"mymod:main\")".to_string())
55 .at_line(root.line)
56 })?
57 .to_string();
58 let vm = root
59 .attr(yog_ids::yui_attr::X_VIEW_MODEL)
60 .unwrap_or_default()
61 .trim()
62 .to_string();
63 if vm.is_empty() {
64 return err(root, "x:ViewModel names the view-model type");
65 }
66 let create = root.attr(yog_ids::yui_attr::X_CREATE).map_or_else(
67 || format!("<{vm} as ::std::default::Default>::default()"),
68 str::to_string,
69 );
70 Ok(Root { id, vm, create })
71}
72
73pub fn generate(
75 root: &Element,
76 name: &str,
77 mod_id: Option<&str>,
78) -> Result<(String, Vec<Diagnostic>), Diagnostic> {
79 let Root { id, vm, create } = read_root(root)?;
80 let frame = yui::screen_frame(root)?;
81 let mut g = VmGen::new(format!("{name}.yui"), mod_id, yui::screen_theme(root)?);
82 for c in root
83 .children
84 .iter()
85 .filter(|c| c.name == yog_ids::yui_vm::SCREEN_RESOURCES)
86 {
87 g.collect_resources(c)?;
88 }
89 let mut content = root.clone();
90 content.attrs.retain(|(k, _)| k != yog_ids::yui_attr::ID);
91 let mut ctx = Ctx::new(Scope::Screen, VIEW_FIELDS, VIEW_TREE, SCREEN_OWNER);
92 let tree = g.element(&mut ctx, &content, &[], None)?;
93 let (expr, ids) = yui::static_widget(&tree, false, 0, mod_id)?;
94 g.absorb(ids);
95 let mut view_fields = String::new();
96 let mut view_inits = String::new();
97 for (n, ty, init) in &g.view_fields {
98 let _ = writeln!(view_fields, " {n}: {ty},");
99 let _ = writeln!(view_inits, " {n}: {init},");
100 }
101 let dispatch: String = g
102 .dispatch
103 .iter()
104 .map(|(pat, body)| {
105 format!(" {pat} => {{\n {body}\n }}\n")
106 })
107 .collect();
108 let values = [
109 ("NAME", name.to_string()),
110 ("VM", vm),
111 ("CODEGEN", CODEGEN.to_string()),
112 ("ID_LIT", format!("{id:?}")),
113 ("WIDTH", f32_lit(frame.width)),
114 ("HEIGHT", f32_lit(frame.height)),
115 ("IDS", yui::str_consts(g.ids.as_slice())),
116 ("TEMPLATES", std::mem::take(&mut g.templates)),
117 ("VIEW_FIELDS", view_fields),
118 ("ANCHOR", yui::pascal(frame.anchor)),
119 ("MODAL", frame.modal.to_string()),
120 ("PAUSE", frame.pause.to_string()),
121 ("LIVE", frame.live.to_string()),
122 ("UI_USE", UI_USE.to_string()),
123 ("EXPR", expr),
124 ("PARTS", std::mem::take(&mut g.parts)),
125 ("VIEW_INITS", view_inits),
126 ("UPDATE", ctx.update),
127 ("DISPATCH", dispatch),
128 ("CREATE", create),
129 ];
130 Ok((fill(TEMPLATE, &values), Vec::new()))
131}
132
133#[cfg(test)]
134mod tests;