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