1use std::borrow::Cow;
5use std::collections::{BTreeMap, HashMap};
6use std::fmt::Display;
7use std::rc::Rc;
8
9use itertools::Itertools;
10
11use smol_str::SmolStr;
12
13use crate::expression_tree::{BuiltinFunction, Expression, Unit};
14use crate::object_tree::{Component, PropertyVisibility};
15use crate::parser::syntax_nodes;
16use crate::typeregister::TypeRegister;
17
18#[derive(Debug, Clone, Default)]
19pub enum Type {
20 #[default]
22 Invalid,
23 Void,
25 InferredProperty,
27 InferredCallback,
29
30 Callback(Rc<Function>),
31 Function(Rc<Function>),
32
33 ComponentFactory,
34
35 Float32,
37 Int32,
38 String,
39 Color,
40 Duration,
41 PhysicalLength,
42 LogicalLength,
43 Rem,
44 Angle,
45 Percent,
46 Image,
47 Bool,
48 Model,
50 PathData, Easing,
52 Brush,
53 Array(Rc<Type>),
55 Struct(Rc<Struct>),
56 Enumeration(Rc<Enumeration>),
57
58 UnitProduct(Vec<(Unit, i8)>),
62
63 ElementReference,
64
65 LayoutCache,
67}
68
69impl core::cmp::PartialEq for Type {
70 fn eq(&self, other: &Self) -> bool {
71 match self {
72 Type::Invalid => matches!(other, Type::Invalid),
73 Type::Void => matches!(other, Type::Void),
74 Type::InferredProperty => matches!(other, Type::InferredProperty),
75 Type::InferredCallback => matches!(other, Type::InferredCallback),
76 Type::Callback(lhs) => {
77 matches!(other, Type::Callback(rhs) if lhs == rhs)
78 }
79 Type::Function(lhs) => {
80 matches!(other, Type::Function(rhs) if lhs == rhs)
81 }
82 Type::ComponentFactory => matches!(other, Type::ComponentFactory),
83 Type::Float32 => matches!(other, Type::Float32),
84 Type::Int32 => matches!(other, Type::Int32),
85 Type::String => matches!(other, Type::String),
86 Type::Color => matches!(other, Type::Color),
87 Type::Duration => matches!(other, Type::Duration),
88 Type::Angle => matches!(other, Type::Angle),
89 Type::PhysicalLength => matches!(other, Type::PhysicalLength),
90 Type::LogicalLength => matches!(other, Type::LogicalLength),
91 Type::Rem => matches!(other, Type::Rem),
92 Type::Percent => matches!(other, Type::Percent),
93 Type::Image => matches!(other, Type::Image),
94 Type::Bool => matches!(other, Type::Bool),
95 Type::Model => matches!(other, Type::Model),
96 Type::PathData => matches!(other, Type::PathData),
97 Type::Easing => matches!(other, Type::Easing),
98 Type::Brush => matches!(other, Type::Brush),
99 Type::Array(a) => matches!(other, Type::Array(b) if a == b),
100 Type::Struct(lhs) => {
101 matches!(other, Type::Struct(rhs) if lhs.fields == rhs.fields && lhs.name == rhs.name)
102 }
103 Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
104 Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
105 Type::ElementReference => matches!(other, Type::ElementReference),
106 Type::LayoutCache => matches!(other, Type::LayoutCache),
107 }
108 }
109}
110
111impl Display for Type {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 Type::Invalid => write!(f, "<error>"),
115 Type::Void => write!(f, "void"),
116 Type::InferredProperty => write!(f, "?"),
117 Type::InferredCallback => write!(f, "callback"),
118 Type::Callback(callback) => {
119 write!(f, "callback")?;
120 if !callback.args.is_empty() {
121 write!(f, "(")?;
122 for (i, arg) in callback.args.iter().enumerate() {
123 if i > 0 {
124 write!(f, ",")?;
125 }
126 write!(f, "{arg}")?;
127 }
128 write!(f, ")")?
129 }
130 write!(f, "-> {}", callback.return_type)?;
131 Ok(())
132 }
133 Type::ComponentFactory => write!(f, "component-factory"),
134 Type::Function(function) => {
135 write!(f, "function(")?;
136 for (i, arg) in function.args.iter().enumerate() {
137 if i > 0 {
138 write!(f, ",")?;
139 }
140 write!(f, "{arg}")?;
141 }
142 write!(f, ") -> {}", function.return_type)
143 }
144 Type::Float32 => write!(f, "float"),
145 Type::Int32 => write!(f, "int"),
146 Type::String => write!(f, "string"),
147 Type::Duration => write!(f, "duration"),
148 Type::Angle => write!(f, "angle"),
149 Type::PhysicalLength => write!(f, "physical-length"),
150 Type::LogicalLength => write!(f, "length"),
151 Type::Rem => write!(f, "relative-font-size"),
152 Type::Percent => write!(f, "percent"),
153 Type::Color => write!(f, "color"),
154 Type::Image => write!(f, "image"),
155 Type::Bool => write!(f, "bool"),
156 Type::Model => write!(f, "model"),
157 Type::Array(t) => write!(f, "[{t}]"),
158 Type::Struct(t) => write!(f, "{t}"),
159 Type::PathData => write!(f, "pathdata"),
160 Type::Easing => write!(f, "easing"),
161 Type::Brush => write!(f, "brush"),
162 Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
163 Type::UnitProduct(vec) => {
164 const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
165 let mut x = vec.iter().map(|(unit, power)| {
166 if *power == 1 {
167 return unit.to_string();
168 }
169 let mut res = format!("{}{}", unit, if *power < 0 { "⁻" } else { "" });
170 let value = power.abs().to_string();
171 for x in value.as_bytes() {
172 res.push(POWERS[(x - b'0') as usize]);
173 }
174
175 res
176 });
177 write!(f, "({})", x.join("×"))
178 }
179 Type::ElementReference => write!(f, "element ref"),
180 Type::LayoutCache => write!(f, "layout cache"),
181 }
182 }
183}
184
185impl From<Rc<Struct>> for Type {
186 fn from(value: Rc<Struct>) -> Self {
187 Self::Struct(value)
188 }
189}
190
191impl Type {
192 pub fn is_property_type(&self) -> bool {
194 matches!(
195 self,
196 Self::Float32
197 | Self::Int32
198 | Self::String
199 | Self::Color
200 | Self::ComponentFactory
201 | Self::Duration
202 | Self::Angle
203 | Self::PhysicalLength
204 | Self::LogicalLength
205 | Self::Rem
206 | Self::Percent
207 | Self::Image
208 | Self::Bool
209 | Self::Easing
210 | Self::Enumeration(_)
211 | Self::ElementReference
212 | Self::Struct { .. }
213 | Self::Array(_)
214 | Self::Brush
215 | Self::InferredProperty
216 )
217 }
218
219 pub fn ok_for_public_api(&self) -> bool {
220 !matches!(self, Self::Easing)
221 }
222
223 pub fn as_enum(&self) -> &Rc<Enumeration> {
225 match self {
226 Type::Enumeration(e) => e,
227 _ => panic!("should be an enumeration, bug in compiler pass"),
228 }
229 }
230
231 pub fn can_convert(&self, other: &Self) -> bool {
233 let can_convert_struct = |a: &BTreeMap<SmolStr, Type>, b: &BTreeMap<SmolStr, Type>| {
234 let mut has_more_property = false;
236 for (k, v) in b {
237 match a.get(k) {
238 Some(t) if !t.can_convert(v) => return false,
239 None => has_more_property = true,
240 _ => (),
241 }
242 }
243 if has_more_property {
244 if a.keys().any(|k| !b.contains_key(k)) {
246 return false;
247 }
248 }
249 true
250 };
251 match (self, other) {
252 (a, b) if a == b => true,
253 (_, Type::Invalid)
254 | (_, Type::Void)
255 | (Type::Float32, Type::Int32)
256 | (Type::Float32, Type::String)
257 | (Type::Int32, Type::Float32)
258 | (Type::Int32, Type::String)
259 | (Type::Float32, Type::Model)
260 | (Type::Int32, Type::Model)
261 | (Type::PhysicalLength, Type::LogicalLength)
262 | (Type::LogicalLength, Type::PhysicalLength)
263 | (Type::Rem, Type::LogicalLength)
264 | (Type::Rem, Type::PhysicalLength)
265 | (Type::LogicalLength, Type::Rem)
266 | (Type::PhysicalLength, Type::Rem)
267 | (Type::Percent, Type::Float32)
268 | (Type::Brush, Type::Color)
269 | (Type::Color, Type::Brush) => true,
270 (Type::Array(a), Type::Model) if a.is_property_type() => true,
271 (Type::Struct(a), Type::Struct(b)) => can_convert_struct(&a.fields, &b.fields),
272 (Type::UnitProduct(u), o) => match o.as_unit_product() {
273 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
274 None => false,
275 },
276 (o, Type::UnitProduct(u)) => match o.as_unit_product() {
277 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
278 None => false,
279 },
280 _ => false,
281 }
282 }
283
284 pub fn default_unit(&self) -> Option<Unit> {
287 match self {
288 Type::Duration => Some(Unit::Ms),
289 Type::PhysicalLength => Some(Unit::Phx),
290 Type::LogicalLength => Some(Unit::Px),
291 Type::Rem => Some(Unit::Rem),
292 Type::Percent => None,
294 Type::Angle => Some(Unit::Deg),
295 Type::Invalid => None,
296 Type::Void => None,
297 Type::InferredProperty | Type::InferredCallback => None,
298 Type::Callback { .. } => None,
299 Type::ComponentFactory => None,
300 Type::Function { .. } => None,
301 Type::Float32 => None,
302 Type::Int32 => None,
303 Type::String => None,
304 Type::Color => None,
305 Type::Image => None,
306 Type::Bool => None,
307 Type::Model => None,
308 Type::PathData => None,
309 Type::Easing => None,
310 Type::Brush => None,
311 Type::Array(_) => None,
312 Type::Struct { .. } => None,
313 Type::Enumeration(_) => None,
314 Type::UnitProduct(_) => None,
315 Type::ElementReference => None,
316 Type::LayoutCache => None,
317 }
318 }
319
320 pub fn as_unit_product(&self) -> Option<Vec<(Unit, i8)>> {
322 match self {
323 Type::UnitProduct(u) => Some(u.clone()),
324 Type::Float32 | Type::Int32 => Some(Vec::new()),
325 Type::Percent => Some(Vec::new()),
326 _ => self.default_unit().map(|u| vec![(u, 1)]),
327 }
328 }
329}
330
331#[derive(Debug, Clone)]
332pub enum BuiltinPropertyDefault {
333 None,
334 Expr(Expression),
335 WithElement(fn(&crate::object_tree::ElementRc) -> Expression),
337 BuiltinFunction(BuiltinFunction),
339}
340
341impl BuiltinPropertyDefault {
342 pub fn expr(&self, elem: &crate::object_tree::ElementRc) -> Option<Expression> {
343 match self {
344 BuiltinPropertyDefault::None => None,
345 BuiltinPropertyDefault::Expr(expression) => Some(expression.clone()),
346 BuiltinPropertyDefault::WithElement(init_expr) => Some(init_expr(elem)),
347 BuiltinPropertyDefault::BuiltinFunction(..) => {
348 unreachable!("can't get an expression for functions")
349 }
350 }
351 }
352}
353
354#[derive(Debug, Clone)]
356pub struct BuiltinPropertyInfo {
357 pub ty: Type,
359 pub default_value: BuiltinPropertyDefault,
361 pub property_visibility: PropertyVisibility,
362}
363
364impl BuiltinPropertyInfo {
365 pub fn new(ty: Type) -> Self {
366 Self {
367 ty,
368 default_value: BuiltinPropertyDefault::None,
369 property_visibility: PropertyVisibility::InOut,
370 }
371 }
372
373 pub fn is_native_output(&self) -> bool {
374 matches!(self.property_visibility, PropertyVisibility::InOut | PropertyVisibility::Output)
375 }
376}
377
378impl From<BuiltinFunction> for BuiltinPropertyInfo {
379 fn from(function: BuiltinFunction) -> Self {
380 Self {
381 ty: Type::Function(function.ty()),
382 default_value: BuiltinPropertyDefault::BuiltinFunction(function),
383 property_visibility: PropertyVisibility::Public,
384 }
385 }
386}
387
388#[derive(Clone, Debug, derive_more::From)]
390pub enum ElementType {
391 Component(Rc<Component>),
393 Builtin(Rc<BuiltinElement>),
395 Native(Rc<NativeClass>),
397 Error,
399 Global,
401}
402
403impl PartialEq for ElementType {
404 fn eq(&self, other: &Self) -> bool {
405 match (self, other) {
406 (Self::Component(a), Self::Component(b)) => Rc::ptr_eq(a, b),
407 (Self::Builtin(a), Self::Builtin(b)) => Rc::ptr_eq(a, b),
408 (Self::Native(a), Self::Native(b)) => Rc::ptr_eq(a, b),
409 (Self::Error, Self::Error) | (Self::Global, Self::Global) => true,
410 _ => false,
411 }
412 }
413}
414
415impl ElementType {
416 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
417 match self {
418 Self::Component(c) => c.root_element.borrow().lookup_property(name),
419 Self::Builtin(b) => {
420 let resolved_name =
421 if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) {
422 Cow::Owned(alias_name.to_string())
423 } else {
424 Cow::Borrowed(name)
425 };
426 match b.properties.get(resolved_name.as_ref()) {
427 None => {
428 if b.is_non_item_type {
429 PropertyLookupResult::invalid(resolved_name)
430 } else {
431 crate::typeregister::reserved_property(name)
432 }
433 }
434 Some(p) => PropertyLookupResult {
435 resolved_name,
436 property_type: p.ty.clone(),
437 property_visibility: p.property_visibility,
438 declared_pure: None,
439 is_local_to_component: false,
440 is_in_direct_base: false,
441 builtin_function: match &p.default_value {
442 BuiltinPropertyDefault::BuiltinFunction(f) => Some(f.clone()),
443 _ => None,
444 },
445 },
446 }
447 }
448 Self::Native(n) => {
449 let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) {
450 Cow::Owned(alias_name.to_string())
451 } else {
452 Cow::Borrowed(name)
453 };
454 let property_type =
455 n.lookup_property(resolved_name.as_ref()).cloned().unwrap_or_default();
456 PropertyLookupResult {
457 resolved_name,
458 property_type,
459 property_visibility: PropertyVisibility::InOut,
460 declared_pure: None,
461 is_local_to_component: false,
462 is_in_direct_base: false,
463 builtin_function: None,
464 }
465 }
466 _ => PropertyLookupResult::invalid(Cow::Borrowed(name)),
467 }
468 }
469
470 pub fn property_list(&self) -> Vec<(SmolStr, Type)> {
472 match self {
473 Self::Component(c) => {
474 let mut r = c.root_element.borrow().base_type.property_list();
475 r.extend(
476 c.root_element
477 .borrow()
478 .property_declarations
479 .iter()
480 .filter(|(_, d)| d.visibility != PropertyVisibility::Private)
481 .map(|(k, d)| (k.clone(), d.property_type.clone())),
482 );
483 r
484 }
485 Self::Builtin(b) => {
486 b.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
487 }
488 Self::Native(n) => {
489 n.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
490 }
491 _ => Vec::new(),
492 }
493 }
494
495 pub fn lookup_type_for_child_element(
499 &self,
500 name: &str,
501 tr: &TypeRegister,
502 ) -> Result<ElementType, String> {
503 match self {
504 Self::Component(component) => {
505 let base_type = match &*component.child_insertion_point.borrow() {
506 Some(insert_in) => insert_in.parent.borrow().base_type.clone(),
507 None => {
508 let base_type = component.root_element.borrow().base_type.clone();
509 if base_type == tr.empty_type() {
510 return Err(format!("'{}' cannot have children. Only components with @children can have children", component.id));
511 }
512 base_type
513 }
514 };
515 base_type.lookup_type_for_child_element(name, tr)
516 }
517 Self::Builtin(builtin) => {
518 if builtin.disallow_global_types_as_child_elements {
519 if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
520 return Ok(child_type.clone().into());
521 } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
522 return Ok(builtin.clone().into());
523 }
524 let mut valid_children: Vec<_> =
525 builtin.additional_accepted_child_types.keys().cloned().collect();
526 if builtin.additional_accept_self {
527 valid_children.push(builtin.native_class.class_name.clone());
528 }
529 valid_children.sort();
530
531 let err = if valid_children.is_empty() {
532 format!("{} cannot have children elements", builtin.native_class.class_name,)
533 } else {
534 format!(
535 "{} is not allowed within {}. Only {} are valid children",
536 name,
537 builtin.native_class.class_name,
538 valid_children.join(" ")
539 )
540 };
541 return Err(err);
542 }
543 let err = match tr.lookup_element(name) {
544 Err(e) => e,
545 Ok(t) => {
546 if !tr.expose_internal_types
547 && matches!(&t, Self::Builtin(e) if e.is_internal)
548 {
549 format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)")
550 } else {
551 return Ok(t);
552 }
553 }
554 };
555 if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
556 return Ok(child_type.clone().into());
557 } else if builtin.additional_accept_self && name == builtin.native_class.class_name {
558 return Ok(builtin.clone().into());
559 }
560 match tr.lookup(name) {
561 Type::Invalid => Err(err),
562 ty => Err(format!("'{ty}' cannot be used as an element")),
563 }
564 }
565 _ => tr.lookup_element(name).and_then(|t| {
566 if !tr.expose_internal_types && matches!(&t, Self::Builtin(e) if e.is_internal) {
567 Err(format!("Unknown element '{name}'. (The type exists as an internal type, but cannot be accessed in this scope)"))
568 } else {
569 Ok(t)
570 }
571 })
572 }
573 }
574
575 pub fn as_builtin(&self) -> &BuiltinElement {
577 match self {
578 Self::Builtin(b) => b,
579 Self::Component(_) => panic!("This should not happen because of inlining"),
580 _ => panic!("invalid type"),
581 }
582 }
583
584 pub fn as_native(&self) -> &NativeClass {
586 match self {
587 Self::Native(b) => b,
588 Self::Component(_) => {
589 panic!("This should not happen because of native class resolution")
590 }
591 _ => panic!("invalid type"),
592 }
593 }
594
595 pub fn as_component(&self) -> &Rc<Component> {
597 match self {
598 Self::Component(c) => c,
599 _ => panic!("should be a component because of the repeater_component pass"),
600 }
601 }
602
603 pub fn type_name(&self) -> Option<&str> {
605 match self {
606 ElementType::Component(component) => Some(&component.id),
607 ElementType::Builtin(b) => Some(&b.name),
608 ElementType::Native(_) => None, ElementType::Error => None,
610 ElementType::Global => None,
611 }
612 }
613}
614
615impl Display for ElementType {
616 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617 match self {
618 Self::Component(c) => c.id.fmt(f),
619 Self::Builtin(b) => b.name.fmt(f),
620 Self::Native(b) => b.class_name.fmt(f),
621 Self::Error => write!(f, "<error>"),
622 Self::Global => Ok(()),
623 }
624 }
625}
626
627impl Default for ElementType {
628 fn default() -> Self {
629 Self::Error
630 }
631}
632
633#[derive(Debug, Clone, Default)]
634pub struct NativeClass {
635 pub parent: Option<Rc<NativeClass>>,
636 pub class_name: SmolStr,
637 pub cpp_vtable_getter: String,
638 pub properties: HashMap<SmolStr, BuiltinPropertyInfo>,
639 pub deprecated_aliases: HashMap<SmolStr, SmolStr>,
640 pub cpp_type: Option<SmolStr>,
641 pub rust_type_constructor: Option<SmolStr>,
642}
643
644impl NativeClass {
645 pub fn new(class_name: &str) -> Self {
646 let cpp_vtable_getter = format!("SLINT_GET_ITEM_VTABLE({class_name}VTable)");
647 Self {
648 class_name: class_name.into(),
649 cpp_vtable_getter,
650 properties: Default::default(),
651 ..Default::default()
652 }
653 }
654
655 pub fn new_with_properties(
656 class_name: &str,
657 properties: impl IntoIterator<Item = (SmolStr, BuiltinPropertyInfo)>,
658 ) -> Self {
659 let mut class = Self::new(class_name);
660 class.properties = properties.into_iter().collect();
661 class
662 }
663
664 pub fn property_count(&self) -> usize {
665 self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
666 }
667
668 pub fn lookup_property(&self, name: &str) -> Option<&Type> {
669 if let Some(bty) = self.properties.get(name) {
670 Some(&bty.ty)
671 } else if let Some(parent_class) = &self.parent {
672 parent_class.lookup_property(name)
673 } else {
674 None
675 }
676 }
677
678 pub fn lookup_alias(&self, name: &str) -> Option<&str> {
679 if let Some(alias_target) = self.deprecated_aliases.get(name) {
680 Some(alias_target)
681 } else if self.properties.contains_key(name) {
682 None
683 } else if let Some(parent_class) = &self.parent {
684 parent_class.lookup_alias(name)
685 } else {
686 None
687 }
688 }
689}
690
691#[derive(Debug, Clone, Copy, PartialEq, Default)]
692pub enum DefaultSizeBinding {
693 #[default]
695 None,
696 ExpandsToParentGeometry,
698 ImplicitSize,
700}
701
702#[derive(Debug, Clone, Default)]
703pub struct BuiltinElement {
704 pub name: SmolStr,
705 pub native_class: Rc<NativeClass>,
706 pub properties: BTreeMap<SmolStr, BuiltinPropertyInfo>,
707 pub additional_accepted_child_types: HashMap<SmolStr, Rc<BuiltinElement>>,
710 pub additional_accept_self: bool,
712 pub disallow_global_types_as_child_elements: bool,
713 pub is_non_item_type: bool,
715 pub accepts_focus: bool,
716 pub is_global: bool,
717 pub default_size_binding: DefaultSizeBinding,
718 pub is_internal: bool,
720}
721
722impl BuiltinElement {
723 pub fn new(native_class: Rc<NativeClass>) -> Self {
724 Self { name: native_class.class_name.clone(), native_class, ..Default::default() }
725 }
726}
727
728#[derive(PartialEq, Debug)]
729pub struct PropertyLookupResult<'a> {
730 pub resolved_name: std::borrow::Cow<'a, str>,
731 pub property_type: Type,
732 pub property_visibility: PropertyVisibility,
733 pub declared_pure: Option<bool>,
734 pub is_local_to_component: bool,
736 pub is_in_direct_base: bool,
738
739 pub builtin_function: Option<BuiltinFunction>,
741}
742
743impl<'a> PropertyLookupResult<'a> {
744 pub fn is_valid(&self) -> bool {
745 self.property_type != Type::Invalid
746 }
747
748 pub fn is_valid_for_assignment(&self) -> bool {
750 !matches!(
751 (self.property_visibility, self.is_local_to_component),
752 (PropertyVisibility::Private, false)
753 | (PropertyVisibility::Input, true)
754 | (PropertyVisibility::Output, false)
755 )
756 }
757
758 pub fn invalid(resolved_name: Cow<'a, str>) -> Self {
759 Self {
760 resolved_name,
761 property_type: Type::Invalid,
762 property_visibility: PropertyVisibility::Private,
763 declared_pure: None,
764 is_local_to_component: false,
765 is_in_direct_base: false,
766 builtin_function: None,
767 }
768 }
769}
770
771#[derive(Debug, Clone, PartialEq)]
772pub struct Function {
773 pub return_type: Type,
774 pub args: Vec<Type>,
775 pub arg_names: Vec<SmolStr>,
778}
779
780#[derive(Debug, Clone)]
781pub struct Struct {
782 pub fields: BTreeMap<SmolStr, Type>,
783 pub name: Option<SmolStr>,
786 pub node: Option<syntax_nodes::ObjectType>,
788 pub rust_attributes: Option<Vec<SmolStr>>,
790}
791
792impl Display for Struct {
793 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
794 if let Some(name) = &self.name {
795 if let Some(separator_pos) = name.rfind("::") {
796 write!(f, "{}", &name[separator_pos + 2..])
798 } else {
799 write!(f, "{name}")
800 }
801 } else {
802 write!(f, "{{ ")?;
803 for (k, v) in &self.fields {
804 write!(f, "{k}: {v},")?;
805 }
806 write!(f, "}}")
807 }
808 }
809}
810
811#[derive(Debug, Clone)]
812pub struct Enumeration {
813 pub name: SmolStr,
814 pub values: Vec<SmolStr>,
815 pub default_value: usize, pub node: Option<syntax_nodes::EnumDeclaration>,
818}
819
820impl PartialEq for Enumeration {
821 fn eq(&self, other: &Self) -> bool {
822 self.name.eq(&other.name)
823 }
824}
825
826impl Enumeration {
827 pub fn default_value(self: Rc<Self>) -> EnumerationValue {
828 EnumerationValue { value: self.default_value, enumeration: self.clone() }
829 }
830
831 pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
832 self.values.iter().enumerate().find_map(|(idx, name)| {
833 if name == value {
834 Some(EnumerationValue { value: idx, enumeration: self.clone() })
835 } else {
836 None
837 }
838 })
839 }
840}
841
842#[derive(Clone, Debug)]
843pub struct EnumerationValue {
844 pub value: usize, pub enumeration: Rc<Enumeration>,
846}
847
848impl PartialEq for EnumerationValue {
849 fn eq(&self, other: &Self) -> bool {
850 Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
851 }
852}
853
854impl std::fmt::Display for EnumerationValue {
855 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
856 self.enumeration.values[self.value].fmt(f)
857 }
858}
859
860impl EnumerationValue {
861 pub fn to_pascal_case(&self) -> String {
862 crate::generator::to_pascal_case(&self.enumeration.values[self.value])
863 }
864}
865
866#[derive(Debug, PartialEq)]
867pub struct LengthConversionPowers {
868 pub rem_to_px_power: i8,
869 pub px_to_phx_power: i8,
870}
871
872pub fn unit_product_length_conversion(
875 a: &[(Unit, i8)],
876 b: &[(Unit, i8)],
877) -> Option<LengthConversionPowers> {
878 if a.is_empty() && b.is_empty() {
880 return Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 });
881 }
882
883 let mut units = [0i8; 16];
884 for (u, count) in a {
885 units[*u as usize] += count;
886 }
887 for (u, count) in b {
888 units[*u as usize] -= count;
889 }
890
891 if units[Unit::Px as usize] + units[Unit::Phx as usize] + units[Unit::Rem as usize] != 0 {
892 return None;
893 }
894
895 if units[Unit::Rem as usize] != 0
896 && units[Unit::Phx as usize] == -units[Unit::Rem as usize]
897 && units[Unit::Px as usize] == 0
898 {
899 units[Unit::Px as usize] = -units[Unit::Rem as usize];
900 units[Unit::Phx as usize] = -units[Unit::Rem as usize];
901 }
902
903 let result = LengthConversionPowers {
904 rem_to_px_power: if units[Unit::Rem as usize] != 0 { units[Unit::Px as usize] } else { 0 },
905 px_to_phx_power: if units[Unit::Px as usize] != 0 { units[Unit::Phx as usize] } else { 0 },
906 };
907
908 units[Unit::Px as usize] = 0;
909 units[Unit::Phx as usize] = 0;
910 units[Unit::Rem as usize] = 0;
911 units.into_iter().all(|x| x == 0).then_some(result)
912}
913
914#[test]
915fn unit_product_length_conversion_test() {
916 use Option::None;
917 use Unit::*;
918 assert_eq!(
919 unit_product_length_conversion(&[], &[]),
920 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: 0 })
921 );
922 assert_eq!(
923 unit_product_length_conversion(&[(Px, 1)], &[(Phx, 1)]),
924 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
925 );
926 assert_eq!(
927 unit_product_length_conversion(&[(Phx, -2)], &[(Px, -2)]),
928 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
929 );
930 assert_eq!(
931 unit_product_length_conversion(&[(Px, 1), (Phx, -2)], &[(Phx, -1)]),
932 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
933 );
934 assert_eq!(
935 unit_product_length_conversion(
936 &[(Deg, 3), (Phx, 2), (Ms, -1)],
937 &[(Phx, 4), (Deg, 3), (Ms, -1), (Px, -2)]
938 ),
939 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
940 );
941 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
942 assert_eq!(unit_product_length_conversion(&[(Deg, 1), (Phx, -2)], &[(Px, -2)]), None);
943 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
944
945 assert_eq!(
946 unit_product_length_conversion(&[(Rem, 1)], &[(Px, 1)]),
947 Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: 0 })
948 );
949 assert_eq!(
950 unit_product_length_conversion(&[(Rem, 1)], &[(Phx, 1)]),
951 Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: -1 })
952 );
953 assert_eq!(
954 unit_product_length_conversion(&[(Rem, 2)], &[(Phx, 2)]),
955 Some(LengthConversionPowers { rem_to_px_power: -2, px_to_phx_power: -2 })
956 );
957}