1use smol_str::SmolStr;
13use std::collections::{BTreeSet, HashSet, VecDeque};
14use std::rc::{Rc, Weak};
15
16use crate::CompilerConfiguration;
17use crate::expression_tree::{BindingExpression, Expression};
18use crate::langtype::{BuiltinStruct, ElementType, StructName};
19use crate::namedreference::NamedReference;
20use crate::object_tree::{Component, Document, ElementRc};
21
22#[cfg(feature = "cpp")]
23pub mod cpp;
24#[cfg(feature = "cpp")]
25pub mod cpp_live_preview;
26#[cfg(feature = "rust")]
27pub mod rust;
28#[cfg(feature = "rust")]
29pub mod rust_live_preview;
30#[cfg(feature = "slint-sc")]
31pub mod slint_sc;
32
33#[cfg(feature = "python")]
34pub mod python;
35
36#[derive(Clone, Debug, PartialEq)]
37pub enum OutputFormat {
38 #[cfg(feature = "cpp")]
39 Cpp(cpp::Config),
40 #[cfg(feature = "rust")]
41 Rust,
42 #[cfg(feature = "slint-sc")]
45 SlintSc,
46 Interpreter,
47 Llr,
48 #[cfg(feature = "python")]
49 Python,
50}
51
52impl OutputFormat {
53 pub fn guess_from_extension(path: &std::path::Path) -> Option<Self> {
54 match path.extension().and_then(|ext| ext.to_str()) {
55 #[cfg(feature = "cpp")]
56 Some("cpp") | Some("cxx") | Some("h") | Some("hpp") => {
57 Some(Self::Cpp(cpp::Config::default()))
58 }
59 #[cfg(feature = "rust")]
60 Some("rs") => Some(Self::Rust),
61 #[cfg(feature = "python")]
62 Some("py") => Some(Self::Python),
63 _ => None,
64 }
65 }
66}
67
68impl std::str::FromStr for OutputFormat {
69 type Err = String;
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 match s {
72 #[cfg(feature = "cpp")]
73 "cpp" => Ok(Self::Cpp(cpp::Config::default())),
74 #[cfg(feature = "rust")]
75 "rust" => Ok(Self::Rust),
76 #[cfg(feature = "slint-sc")]
77 "slint-sc" | "rust-sc" => Ok(Self::SlintSc),
78 "llr" => Ok(Self::Llr),
79 #[cfg(feature = "python")]
80 "python" => Ok(Self::Python),
81 _ => Err(format!("Unknown output format {s}")),
82 }
83 }
84}
85
86pub fn generate(
87 format: OutputFormat,
88 destination: &mut impl std::io::Write,
89 destination_path: Option<&std::path::Path>,
90 doc: &Document,
91 compiler_config: &CompilerConfiguration,
92) -> std::io::Result<()> {
93 #![allow(unused_variables)]
94 #![allow(unreachable_code)]
95
96 match format {
97 #[cfg(feature = "cpp")]
98 OutputFormat::Cpp(config) => {
99 let output = cpp::generate(doc, config, compiler_config)?;
100 write!(destination, "{output}")?;
101 }
102 #[cfg(feature = "rust")]
103 OutputFormat::Rust => {
104 let output = rust::generate(doc, compiler_config)?;
105 write!(destination, "{output}")?;
106 }
107 #[cfg(feature = "slint-sc")]
108 OutputFormat::SlintSc => {
109 let output = slint_sc::generate(doc, compiler_config)?;
110 write!(destination, "{output}")?;
111 }
112 OutputFormat::Interpreter => {
113 return Err(std::io::Error::other(
114 "Unsupported output format: The interpreter is not a valid output format yet.",
115 )); }
117 OutputFormat::Llr => {
118 let root = crate::llr::lower_to_item_tree::lower_to_item_tree(doc, compiler_config);
119 let mut output = String::new();
120 crate::llr::pretty_print::pretty_print(&root, &mut output).unwrap();
121 write!(destination, "{output}")?;
122 }
123 #[cfg(feature = "python")]
124 OutputFormat::Python => {
125 let output = python::generate(doc, compiler_config, destination_path)?;
126 write!(destination, "{output}")?;
127 }
128 }
129 Ok(())
130}
131
132pub trait ItemTreeBuilder {
135 type SubComponentState: Clone;
137
138 fn push_repeated_item(
139 &mut self,
140 item: &crate::object_tree::ElementRc,
141 repeater_count: u32,
142 parent_index: u32,
143 component_state: &Self::SubComponentState,
144 );
145 fn push_native_item(
146 &mut self,
147 item: &ElementRc,
148 children_offset: u32,
149 parent_index: u32,
150 component_state: &Self::SubComponentState,
151 );
152 fn enter_component(
155 &mut self,
156 item: &ElementRc,
157 sub_component: &Rc<Component>,
158 children_offset: u32,
159 component_state: &Self::SubComponentState,
160 ) -> Self::SubComponentState;
161 fn enter_component_children(
163 &mut self,
164 item: &ElementRc,
165 repeater_count: u32,
166 component_state: &Self::SubComponentState,
167 sub_component_state: &Self::SubComponentState,
168 );
169}
170
171pub fn build_item_tree<T: ItemTreeBuilder>(
173 root_component: &Rc<Component>,
174 initial_state: &T::SubComponentState,
175 builder: &mut T,
176) {
177 if let Some(sub_component) = root_component.root_element.borrow().sub_component() {
178 assert!(root_component.root_element.borrow().children.is_empty());
179 let sub_compo_state =
180 builder.enter_component(&root_component.root_element, sub_component, 1, initial_state);
181 builder.enter_component_children(
182 &root_component.root_element,
183 0,
184 initial_state,
185 &sub_compo_state,
186 );
187 build_item_tree::<T>(sub_component, &sub_compo_state, builder);
188 } else {
189 let mut repeater_count = 0;
190 visit_item(initial_state, &root_component.root_element, 1, &mut repeater_count, 0, builder);
191
192 visit_children(
193 initial_state,
194 &root_component.root_element.borrow().children,
195 root_component,
196 &root_component.root_element,
197 0,
198 0,
199 1,
200 1,
201 &mut repeater_count,
202 builder,
203 );
204 }
205
206 fn item_sub_tree_size(e: &ElementRc) -> usize {
210 let mut count = e.borrow().children.len();
211 if let Some(sub_component) = e.borrow().sub_component() {
212 count += item_sub_tree_size(&sub_component.root_element);
213 }
214 for i in &e.borrow().children {
215 count += item_sub_tree_size(i);
216 }
217 count
218 }
219
220 fn visit_children<T: ItemTreeBuilder>(
221 state: &T::SubComponentState,
222 children: &[ElementRc],
223 _component: &Rc<Component>,
224 parent_item: &ElementRc,
225 parent_index: u32,
226 relative_parent_index: u32,
227 children_offset: u32,
228 relative_children_offset: u32,
229 repeater_count: &mut u32,
230 builder: &mut T,
231 ) {
232 debug_assert_eq!(
233 relative_parent_index,
234 *parent_item.borrow().item_index.get().unwrap_or(&parent_index)
235 );
236
237 if children.is_empty()
251 && let Some(nested_subcomponent) = parent_item.borrow().sub_component()
252 {
253 let sub_component_state =
254 builder.enter_component(parent_item, nested_subcomponent, children_offset, state);
255 visit_children(
256 &sub_component_state,
257 &nested_subcomponent.root_element.borrow().children,
258 nested_subcomponent,
259 &nested_subcomponent.root_element,
260 parent_index,
261 relative_parent_index,
262 children_offset,
263 relative_children_offset,
264 repeater_count,
265 builder,
266 );
267 return;
268 }
269
270 let mut offset = children_offset + children.len() as u32;
271
272 let mut sub_component_states = VecDeque::new();
273
274 for child in children.iter() {
275 if let Some(sub_component) = child.borrow().sub_component() {
276 let sub_component_state =
277 builder.enter_component(child, sub_component, offset, state);
278 visit_item(
279 &sub_component_state,
280 &sub_component.root_element,
281 offset,
282 repeater_count,
283 parent_index,
284 builder,
285 );
286 sub_component_states.push_back(sub_component_state);
287 } else {
288 visit_item(state, child, offset, repeater_count, parent_index, builder);
289 }
290 offset += item_sub_tree_size(child) as u32;
291 }
292
293 let mut offset = children_offset + children.len() as u32;
294 let mut relative_offset = relative_children_offset + children.len() as u32;
295
296 for (i, e) in children.iter().enumerate() {
297 let index = children_offset + i as u32;
298 let relative_index = relative_children_offset + i as u32;
299 if let Some(sub_component) = e.borrow().sub_component() {
300 let sub_tree_state = sub_component_states.pop_front().unwrap();
301 builder.enter_component_children(e, *repeater_count, state, &sub_tree_state);
302 visit_children(
303 &sub_tree_state,
304 &sub_component.root_element.borrow().children,
305 sub_component,
306 &sub_component.root_element,
307 index,
308 0,
309 offset,
310 1,
311 repeater_count,
312 builder,
313 );
314 } else {
315 visit_children(
316 state,
317 &e.borrow().children,
318 _component,
319 e,
320 index,
321 relative_index,
322 offset,
323 relative_offset,
324 repeater_count,
325 builder,
326 );
327 }
328
329 let size = item_sub_tree_size(e) as u32;
330 offset += size;
331 relative_offset += size;
332 }
333 }
334
335 fn visit_item<T: ItemTreeBuilder>(
336 component_state: &T::SubComponentState,
337 item: &ElementRc,
338 children_offset: u32,
339 repeater_count: &mut u32,
340 parent_index: u32,
341 builder: &mut T,
342 ) {
343 if item.borrow().repeated.is_some() {
344 builder.push_repeated_item(item, *repeater_count, parent_index, component_state);
345 *repeater_count += 1;
346 } else {
347 let mut item = item.clone();
348 let mut component_state = component_state.clone();
349 while let Some((base, state)) = {
350 item.borrow().sub_component().map(|c| {
351 (
352 c.root_element.clone(),
353 builder.enter_component(&item, c, children_offset, &component_state),
354 )
355 })
356 } {
357 item = base;
358 component_state = state;
359 }
360 builder.push_native_item(&item, children_offset, parent_index, &component_state)
361 }
362 }
363}
364
365pub fn handle_property_bindings_init(
369 component: &Rc<Component>,
370 mut handle_property: impl FnMut(&ElementRc, &SmolStr, &BindingExpression),
371) {
372 fn handle_property_inner(
373 component: &Weak<Component>,
374 elem: &ElementRc,
375 prop_name: &SmolStr,
376 binding_expression: &BindingExpression,
377 handle_property: &mut impl FnMut(&ElementRc, &SmolStr, &BindingExpression),
378 processed: &mut HashSet<NamedReference>,
379 ) {
380 if elem.borrow().is_component_placeholder {
381 return; }
383 let nr = NamedReference::new(elem, prop_name.clone());
384 if processed.contains(&nr) {
385 return;
386 }
387 processed.insert(nr);
388 if binding_expression.analysis.as_ref().is_some_and(|a| a.is_const) {
389 binding_expression.expression.visit_recursive(&mut |e| {
392 if let Expression::PropertyReference(nr) = e {
393 let elem = nr.element();
394 if Weak::ptr_eq(&elem.borrow().enclosing_component, component)
395 && let Some(be) = elem.borrow().bindings.get(nr.name())
396 {
397 handle_property_inner(
398 component,
399 &elem,
400 nr.name(),
401 &be.borrow(),
402 handle_property,
403 processed,
404 );
405 }
406 }
407 })
408 }
409 handle_property(elem, prop_name, binding_expression);
410 }
411
412 let mut processed = HashSet::new();
413 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
414 for (prop_name, binding_expression) in &elem.borrow().bindings {
415 handle_property_inner(
416 &Rc::downgrade(component),
417 elem,
418 prop_name,
419 &binding_expression.borrow(),
420 &mut handle_property,
421 &mut processed,
422 );
423 }
424 });
425}
426
427pub fn for_each_const_properties(
430 component: &Rc<Component>,
431 mut f: impl FnMut(&ElementRc, &SmolStr),
432) {
433 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |elem: &ElementRc, ()| {
434 if elem.borrow().repeated.is_some() {
435 return;
436 }
437 let mut e = elem.clone();
438 let mut all_prop = BTreeSet::new();
439 loop {
440 all_prop.extend(
441 e.borrow()
442 .property_declarations
443 .iter()
444 .filter(|(_, x)| {
445 x.property_type.is_property_type() &&
446 !matches!( &x.property_type, crate::langtype::Type::Struct(s) if matches!(s.name, StructName::Builtin(BuiltinStruct::StateInfo)))
447 })
448 .map(|(k, _)| k.clone()),
449 );
450 match &e.clone().borrow().base_type {
451 ElementType::Component(c) => {
452 e = c.root_element.clone();
453 }
454 ElementType::Native(n) => {
455 let mut n = n;
456 loop {
457 all_prop.extend(
458 n.properties
459 .iter()
460 .filter(|(k, x)| {
461 x.ty.is_property_type()
462 && !k.starts_with("viewport-")
463 && k.as_str() != "commands"
464 })
465 .map(|(k, _)| k.clone()),
466 );
467 match n.parent.as_ref() {
468 Some(p) => n = p,
469 None => break,
470 }
471 }
472 break;
473 }
474 ElementType::Builtin(_) => {
475 unreachable!("builtin element should have been resolved")
476 }
477 ElementType::Global | ElementType::Interface | ElementType::Error => break,
478 }
479 }
480 for c in all_prop {
481 if NamedReference::new(elem, c.clone()).is_constant() {
482 f(elem, &c);
483 }
484 }
485 });
486}
487
488pub fn to_pascal_case(str: &str) -> String {
490 let mut result = Vec::with_capacity(str.len());
491 let mut next_upper = true;
492 for x in str.as_bytes() {
493 if *x == b'-' {
494 next_upper = true;
495 } else if next_upper {
496 result.push(x.to_ascii_uppercase());
497 next_upper = false;
498 } else {
499 result.push(*x);
500 }
501 }
502 String::from_utf8(result).unwrap()
503}
504
505pub fn to_kebab_case(str: &str) -> String {
507 let mut result = Vec::with_capacity(str.len());
508 for x in str.as_bytes() {
509 if x.is_ascii_uppercase() {
510 if !result.is_empty() {
511 result.push(b'-');
512 }
513 result.push(x.to_ascii_lowercase());
514 } else {
515 result.push(*x);
516 }
517 }
518 String::from_utf8(result).unwrap()
519}
520
521#[test]
522fn case_conversions() {
523 assert_eq!(to_kebab_case("HelloWorld"), "hello-world");
524 assert_eq!(to_pascal_case("hello-world"), "HelloWorld");
525}