1use std::borrow::Cow;
5use std::collections::{BTreeMap, HashMap, HashSet};
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) => {
159 if let Some(name) = &t.name {
160 if let Some(separator_pos) = name.rfind("::") {
161 write!(f, "{}", &name[separator_pos + 2..])
163 } else {
164 write!(f, "{}", name)
165 }
166 } else {
167 write!(f, "{{ ")?;
168 for (k, v) in &t.fields {
169 write!(f, "{}: {},", k, v)?;
170 }
171 write!(f, "}}")
172 }
173 }
174 Type::PathData => write!(f, "pathdata"),
175 Type::Easing => write!(f, "easing"),
176 Type::Brush => write!(f, "brush"),
177 Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
178 Type::UnitProduct(vec) => {
179 const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
180 let mut x = vec.iter().map(|(unit, power)| {
181 if *power == 1 {
182 return unit.to_string();
183 }
184 let mut res = format!("{}{}", unit, if *power < 0 { "⁻" } else { "" });
185 let value = power.abs().to_string();
186 for x in value.as_bytes() {
187 res.push(POWERS[(x - b'0') as usize]);
188 }
189
190 res
191 });
192 write!(f, "({})", x.join("×"))
193 }
194 Type::ElementReference => write!(f, "element ref"),
195 Type::LayoutCache => write!(f, "layout cache"),
196 }
197 }
198}
199
200impl Type {
201 pub fn is_property_type(&self) -> bool {
203 matches!(
204 self,
205 Self::Float32
206 | Self::Int32
207 | Self::String
208 | Self::Color
209 | Self::ComponentFactory
210 | Self::Duration
211 | Self::Angle
212 | Self::PhysicalLength
213 | Self::LogicalLength
214 | Self::Rem
215 | Self::Percent
216 | Self::Image
217 | Self::Bool
218 | Self::Easing
219 | Self::Enumeration(_)
220 | Self::ElementReference
221 | Self::Struct { .. }
222 | Self::Array(_)
223 | Self::Brush
224 | Self::InferredProperty
225 )
226 }
227
228 pub fn ok_for_public_api(&self) -> bool {
229 !matches!(self, Self::Easing)
230 }
231
232 pub fn as_enum(&self) -> &Rc<Enumeration> {
234 match self {
235 Type::Enumeration(e) => e,
236 _ => panic!("should be an enumeration, bug in compiler pass"),
237 }
238 }
239
240 pub fn can_convert(&self, other: &Self) -> bool {
242 let can_convert_struct = |a: &BTreeMap<SmolStr, Type>, b: &BTreeMap<SmolStr, Type>| {
243 let mut has_more_property = false;
245 for (k, v) in b {
246 match a.get(k) {
247 Some(t) if !t.can_convert(v) => return false,
248 None => has_more_property = true,
249 _ => (),
250 }
251 }
252 if has_more_property {
253 if a.keys().any(|k| !b.contains_key(k)) {
255 return false;
256 }
257 }
258 true
259 };
260 match (self, other) {
261 (a, b) if a == b => true,
262 (_, Type::Invalid)
263 | (_, Type::Void)
264 | (Type::Float32, Type::Int32)
265 | (Type::Float32, Type::String)
266 | (Type::Int32, Type::Float32)
267 | (Type::Int32, Type::String)
268 | (Type::Float32, Type::Model)
269 | (Type::Int32, Type::Model)
270 | (Type::PhysicalLength, Type::LogicalLength)
271 | (Type::LogicalLength, Type::PhysicalLength)
272 | (Type::Rem, Type::LogicalLength)
273 | (Type::Rem, Type::PhysicalLength)
274 | (Type::LogicalLength, Type::Rem)
275 | (Type::PhysicalLength, Type::Rem)
276 | (Type::Percent, Type::Float32)
277 | (Type::Brush, Type::Color)
278 | (Type::Color, Type::Brush) => true,
279 (Type::Array(a), Type::Model) if a.is_property_type() => true,
280 (Type::Struct(a), Type::Struct(b)) => can_convert_struct(&a.fields, &b.fields),
281 (Type::UnitProduct(u), o) => match o.as_unit_product() {
282 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
283 None => false,
284 },
285 (o, Type::UnitProduct(u)) => match o.as_unit_product() {
286 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
287 None => false,
288 },
289 _ => false,
290 }
291 }
292
293 pub fn default_unit(&self) -> Option<Unit> {
296 match self {
297 Type::Duration => Some(Unit::Ms),
298 Type::PhysicalLength => Some(Unit::Phx),
299 Type::LogicalLength => Some(Unit::Px),
300 Type::Rem => Some(Unit::Rem),
301 Type::Percent => None,
303 Type::Angle => Some(Unit::Deg),
304 Type::Invalid => None,
305 Type::Void => None,
306 Type::InferredProperty | Type::InferredCallback => None,
307 Type::Callback { .. } => None,
308 Type::ComponentFactory => None,
309 Type::Function { .. } => None,
310 Type::Float32 => None,
311 Type::Int32 => None,
312 Type::String => None,
313 Type::Color => None,
314 Type::Image => None,
315 Type::Bool => None,
316 Type::Model => None,
317 Type::PathData => None,
318 Type::Easing => None,
319 Type::Brush => None,
320 Type::Array(_) => None,
321 Type::Struct { .. } => None,
322 Type::Enumeration(_) => None,
323 Type::UnitProduct(_) => None,
324 Type::ElementReference => None,
325 Type::LayoutCache => None,
326 }
327 }
328
329 pub fn as_unit_product(&self) -> Option<Vec<(Unit, i8)>> {
331 match self {
332 Type::UnitProduct(u) => Some(u.clone()),
333 Type::Float32 | Type::Int32 => Some(Vec::new()),
334 Type::Percent => Some(Vec::new()),
335 _ => self.default_unit().map(|u| vec![(u, 1)]),
336 }
337 }
338}
339
340#[derive(Debug, Clone)]
341
342pub enum BuiltinPropertyDefault {
343 None,
344 Expr(Expression),
345 Fn(fn(&crate::object_tree::ElementRc) -> Expression),
346}
347
348impl BuiltinPropertyDefault {
349 pub fn expr(&self, elem: &crate::object_tree::ElementRc) -> Option<Expression> {
350 match self {
351 BuiltinPropertyDefault::None => None,
352 BuiltinPropertyDefault::Expr(expression) => Some(expression.clone()),
353 BuiltinPropertyDefault::Fn(init_expr) => Some(init_expr(elem)),
354 }
355 }
356}
357
358#[derive(Debug, Clone)]
360pub struct BuiltinPropertyInfo {
361 pub ty: Type,
363 pub default_value: BuiltinPropertyDefault,
365 pub property_visibility: PropertyVisibility,
366}
367
368impl BuiltinPropertyInfo {
369 pub fn new(ty: Type) -> Self {
370 Self {
371 ty,
372 default_value: BuiltinPropertyDefault::None,
373 property_visibility: PropertyVisibility::InOut,
374 }
375 }
376
377 pub fn is_native_output(&self) -> bool {
378 matches!(self.property_visibility, PropertyVisibility::InOut | PropertyVisibility::Output)
379 }
380}
381
382#[derive(Clone, Debug, derive_more::From)]
384pub enum ElementType {
385 Component(Rc<Component>),
387 Builtin(Rc<BuiltinElement>),
389 Native(Rc<NativeClass>),
391 Error,
393 Global,
395}
396
397impl PartialEq for ElementType {
398 fn eq(&self, other: &Self) -> bool {
399 match (self, other) {
400 (Self::Component(a), Self::Component(b)) => Rc::ptr_eq(a, b),
401 (Self::Builtin(a), Self::Builtin(b)) => Rc::ptr_eq(a, b),
402 (Self::Native(a), Self::Native(b)) => Rc::ptr_eq(a, b),
403 (Self::Error, Self::Error) | (Self::Global, Self::Global) => true,
404 _ => false,
405 }
406 }
407}
408
409impl ElementType {
410 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
411 match self {
412 Self::Component(c) => c.root_element.borrow().lookup_property(name),
413 Self::Builtin(b) => {
414 let resolved_name =
415 if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) {
416 Cow::Owned(alias_name.to_string())
417 } else {
418 Cow::Borrowed(name)
419 };
420 match b.properties.get(resolved_name.as_ref()) {
421 None => {
422 if b.is_non_item_type {
423 PropertyLookupResult {
424 resolved_name,
425 property_type: Type::Invalid,
426 property_visibility: PropertyVisibility::Private,
427 declared_pure: None,
428 is_local_to_component: false,
429 is_in_direct_base: false,
430 }
431 } else {
432 crate::typeregister::reserved_property(name)
433 }
434 }
435 Some(p) => PropertyLookupResult {
436 resolved_name,
437 property_type: p.ty.clone(),
438 property_visibility: p.property_visibility,
439 declared_pure: None,
440 is_local_to_component: false,
441 is_in_direct_base: false,
442 },
443 }
444 }
445 Self::Native(n) => {
446 let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) {
447 Cow::Owned(alias_name.to_string())
448 } else {
449 Cow::Borrowed(name)
450 };
451 let property_type =
452 n.lookup_property(resolved_name.as_ref()).cloned().unwrap_or_default();
453 PropertyLookupResult {
454 resolved_name,
455 property_type,
456 property_visibility: PropertyVisibility::InOut,
457 declared_pure: None,
458 is_local_to_component: false,
459 is_in_direct_base: false,
460 }
461 }
462 _ => PropertyLookupResult {
463 resolved_name: Cow::Borrowed(name),
464 property_type: Type::Invalid,
465 property_visibility: PropertyVisibility::Private,
466 declared_pure: None,
467 is_local_to_component: false,
468 is_in_direct_base: false,
469 },
470 }
471 }
472
473 pub fn property_list(&self) -> Vec<(SmolStr, Type)> {
475 match self {
476 Self::Component(c) => {
477 let mut r = c.root_element.borrow().base_type.property_list();
478 r.extend(
479 c.root_element
480 .borrow()
481 .property_declarations
482 .iter()
483 .map(|(k, d)| (k.clone(), d.property_type.clone())),
484 );
485 r
486 }
487 Self::Builtin(b) => {
488 b.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
489 }
490 Self::Native(n) => {
491 n.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
492 }
493 _ => Vec::new(),
494 }
495 }
496
497 pub fn accepts_child_element(
502 &self,
503 name: &str,
504 tr: &TypeRegister,
505 ) -> Result<Option<ElementType>, String> {
506 match self {
507 Self::Component(component) if component.child_insertion_point.borrow().is_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 return base_type.accepts_child_element(name, tr);
513 }
514 Self::Builtin(builtin) => {
515 if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
516 return Ok(Some(child_type.clone()));
517 }
518 if builtin.disallow_global_types_as_child_elements {
519 let mut valid_children: Vec<_> =
520 builtin.additional_accepted_child_types.keys().cloned().collect();
521 valid_children.sort();
522
523 let err = if valid_children.is_empty() {
524 format!("{} cannot have children elements", builtin.native_class.class_name,)
525 } else {
526 format!(
527 "{} is not allowed within {}. Only {} are valid children",
528 name,
529 builtin.native_class.class_name,
530 valid_children.join(" ")
531 )
532 };
533
534 return Err(err);
535 }
536 }
537 _ => {}
538 };
539 Ok(None)
540 }
541
542 pub fn lookup_type_for_child_element(
546 &self,
547 name: &str,
548 tr: &TypeRegister,
549 ) -> Result<ElementType, String> {
550 if let Some(ct) = self.accepts_child_element(name, tr)? {
551 return Ok(ct);
552 }
553
554 tr.lookup_element(name).and_then(|t| {
555 if !tr.expose_internal_types && matches!(&t, Self::Builtin(e) if e.is_internal) {
556 Err(format!("Unknown element '{}'. (The type exist as an internal type, but cannot be accessed in this scope)", name))
557 } else {
558 Ok(t)
559 }
560 }).map_err(|s| {
561 match tr.lookup(name) {
562 Type::Invalid => s,
563 ty => format!("'{ty}' cannot be used as an element")
564 }
565 })
566 }
567
568 pub fn lookup_member_function(&self, name: &str) -> Option<BuiltinFunction> {
569 match self {
570 Self::Builtin(builtin) => builtin
571 .member_functions
572 .get(name)
573 .cloned()
574 .or_else(|| crate::typeregister::reserved_member_function(name)),
575 Self::Component(component) => {
576 component.root_element.borrow().base_type.lookup_member_function(name)
577 }
578 _ => None,
579 }
580 }
581
582 pub fn collect_contextual_types(
583 &self,
584 context_restricted_types: &mut HashMap<SmolStr, HashSet<SmolStr>>,
585 ) {
586 let builtin = match self {
587 Self::Builtin(ty) => ty,
588 _ => return,
589 };
590 for (accepted_child_type_name, accepted_child_type) in
591 builtin.additional_accepted_child_types.iter()
592 {
593 context_restricted_types
594 .entry(accepted_child_type_name.clone())
595 .or_default()
596 .insert(builtin.native_class.class_name.clone());
597
598 accepted_child_type.collect_contextual_types(context_restricted_types);
599 }
600 }
601
602 pub fn as_builtin(&self) -> &BuiltinElement {
604 match self {
605 Self::Builtin(b) => b,
606 Self::Component(_) => panic!("This should not happen because of inlining"),
607 _ => panic!("invalid type"),
608 }
609 }
610
611 pub fn as_native(&self) -> &NativeClass {
613 match self {
614 Self::Native(b) => b,
615 Self::Component(_) => {
616 panic!("This should not happen because of native class resolution")
617 }
618 _ => panic!("invalid type"),
619 }
620 }
621
622 pub fn as_component(&self) -> &Rc<Component> {
624 match self {
625 Self::Component(c) => c,
626 _ => panic!("should be a component because of the repeater_component pass"),
627 }
628 }
629
630 pub fn type_name(&self) -> Option<&str> {
632 match self {
633 ElementType::Component(component) => Some(&component.id),
634 ElementType::Builtin(b) => Some(&b.name),
635 ElementType::Native(_) => None, ElementType::Error => None,
637 ElementType::Global => None,
638 }
639 }
640}
641
642impl Display for ElementType {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 match self {
645 Self::Component(c) => c.id.fmt(f),
646 Self::Builtin(b) => b.name.fmt(f),
647 Self::Native(b) => b.class_name.fmt(f),
648 Self::Error => write!(f, "<error>"),
649 Self::Global => Ok(()),
650 }
651 }
652}
653
654impl Default for ElementType {
655 fn default() -> Self {
656 Self::Error
657 }
658}
659
660#[derive(Debug, Clone, Default)]
661pub struct NativeClass {
662 pub parent: Option<Rc<NativeClass>>,
663 pub class_name: SmolStr,
664 pub cpp_vtable_getter: String,
665 pub properties: HashMap<SmolStr, BuiltinPropertyInfo>,
666 pub deprecated_aliases: HashMap<SmolStr, SmolStr>,
667 pub cpp_type: Option<SmolStr>,
668 pub rust_type_constructor: Option<SmolStr>,
669}
670
671impl NativeClass {
672 pub fn new(class_name: &str) -> Self {
673 let cpp_vtable_getter = format!("SLINT_GET_ITEM_VTABLE({}VTable)", class_name);
674 Self {
675 class_name: class_name.into(),
676 cpp_vtable_getter,
677 properties: Default::default(),
678 ..Default::default()
679 }
680 }
681
682 pub fn new_with_properties(
683 class_name: &str,
684 properties: impl IntoIterator<Item = (SmolStr, BuiltinPropertyInfo)>,
685 ) -> Self {
686 let mut class = Self::new(class_name);
687 class.properties = properties.into_iter().collect();
688 class
689 }
690
691 pub fn property_count(&self) -> usize {
692 self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
693 }
694
695 pub fn lookup_property(&self, name: &str) -> Option<&Type> {
696 if let Some(bty) = self.properties.get(name) {
697 Some(&bty.ty)
698 } else if let Some(parent_class) = &self.parent {
699 parent_class.lookup_property(name)
700 } else {
701 None
702 }
703 }
704
705 pub fn lookup_alias(&self, name: &str) -> Option<&str> {
706 if let Some(alias_target) = self.deprecated_aliases.get(name) {
707 Some(alias_target)
708 } else if self.properties.contains_key(name) {
709 None
710 } else if let Some(parent_class) = &self.parent {
711 parent_class.lookup_alias(name)
712 } else {
713 None
714 }
715 }
716}
717
718#[derive(Debug, Clone, Copy, PartialEq, Default)]
719pub enum DefaultSizeBinding {
720 #[default]
722 None,
723 ExpandsToParentGeometry,
725 ImplicitSize,
727}
728
729#[derive(Debug, Clone, Default)]
730pub struct BuiltinElement {
731 pub name: SmolStr,
732 pub native_class: Rc<NativeClass>,
733 pub properties: BTreeMap<SmolStr, BuiltinPropertyInfo>,
734 pub additional_accepted_child_types: HashMap<SmolStr, ElementType>,
735 pub disallow_global_types_as_child_elements: bool,
736 pub is_non_item_type: bool,
738 pub accepts_focus: bool,
739 pub member_functions: HashMap<SmolStr, BuiltinFunction>,
740 pub is_global: bool,
741 pub default_size_binding: DefaultSizeBinding,
742 pub is_internal: bool,
744}
745
746impl BuiltinElement {
747 pub fn new(native_class: Rc<NativeClass>) -> Self {
748 Self { name: native_class.class_name.clone(), native_class, ..Default::default() }
749 }
750}
751
752#[derive(PartialEq, Debug)]
753pub struct PropertyLookupResult<'a> {
754 pub resolved_name: std::borrow::Cow<'a, str>,
755 pub property_type: Type,
756 pub property_visibility: PropertyVisibility,
757 pub declared_pure: Option<bool>,
758 pub is_local_to_component: bool,
760 pub is_in_direct_base: bool,
762}
763
764impl<'a> PropertyLookupResult<'a> {
765 pub fn is_valid(&self) -> bool {
766 self.property_type != Type::Invalid
767 }
768
769 pub fn is_valid_for_assignment(&self) -> bool {
771 !matches!(
772 (self.property_visibility, self.is_local_to_component),
773 (PropertyVisibility::Private, false)
774 | (PropertyVisibility::Input, true)
775 | (PropertyVisibility::Output, false)
776 )
777 }
778}
779
780#[derive(Debug, Clone, PartialEq)]
781pub struct Function {
782 pub return_type: Type,
783 pub args: Vec<Type>,
784 pub arg_names: Vec<SmolStr>,
787}
788
789#[derive(Debug, Clone)]
790pub struct Struct {
791 pub fields: BTreeMap<SmolStr, Type>,
792 pub name: Option<SmolStr>,
795 pub node: Option<syntax_nodes::ObjectType>,
797 pub rust_attributes: Option<Vec<SmolStr>>,
799}
800
801#[derive(Debug, Clone)]
802pub struct Enumeration {
803 pub name: SmolStr,
804 pub values: Vec<SmolStr>,
805 pub default_value: usize, pub node: Option<syntax_nodes::EnumDeclaration>,
808}
809
810impl PartialEq for Enumeration {
811 fn eq(&self, other: &Self) -> bool {
812 self.name.eq(&other.name)
813 }
814}
815
816impl Enumeration {
817 pub fn default_value(self: Rc<Self>) -> EnumerationValue {
818 EnumerationValue { value: self.default_value, enumeration: self.clone() }
819 }
820
821 pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
822 self.values.iter().enumerate().find_map(|(idx, name)| {
823 if name == value {
824 Some(EnumerationValue { value: idx, enumeration: self.clone() })
825 } else {
826 None
827 }
828 })
829 }
830}
831
832#[derive(Clone, Debug)]
833pub struct EnumerationValue {
834 pub value: usize, pub enumeration: Rc<Enumeration>,
836}
837
838impl PartialEq for EnumerationValue {
839 fn eq(&self, other: &Self) -> bool {
840 Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
841 }
842}
843
844impl std::fmt::Display for EnumerationValue {
845 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
846 self.enumeration.values[self.value].fmt(f)
847 }
848}
849
850impl EnumerationValue {
851 pub fn to_pascal_case(&self) -> String {
852 crate::generator::to_pascal_case(&self.enumeration.values[self.value])
853 }
854}
855
856#[derive(Debug, PartialEq)]
857pub struct LengthConversionPowers {
858 pub rem_to_px_power: i8,
859 pub px_to_phx_power: i8,
860}
861
862pub fn unit_product_length_conversion(
865 a: &[(Unit, i8)],
866 b: &[(Unit, i8)],
867) -> Option<LengthConversionPowers> {
868 let mut units = [0i8; 16];
869 for (u, count) in a {
870 units[*u as usize] += count;
871 }
872 for (u, count) in b {
873 units[*u as usize] -= count;
874 }
875
876 if units[Unit::Px as usize] + units[Unit::Phx as usize] + units[Unit::Rem as usize] != 0 {
877 return None;
878 }
879
880 if units[Unit::Rem as usize] != 0
881 && units[Unit::Phx as usize] == -units[Unit::Rem as usize]
882 && units[Unit::Px as usize] == 0
883 {
884 units[Unit::Px as usize] = -units[Unit::Rem as usize];
885 units[Unit::Phx as usize] = -units[Unit::Rem as usize];
886 }
887
888 let result = LengthConversionPowers {
889 rem_to_px_power: if units[Unit::Rem as usize] != 0 { units[Unit::Px as usize] } else { 0 },
890 px_to_phx_power: if units[Unit::Px as usize] != 0 { units[Unit::Phx as usize] } else { 0 },
891 };
892
893 units[Unit::Px as usize] = 0;
894 units[Unit::Phx as usize] = 0;
895 units[Unit::Rem as usize] = 0;
896 units.into_iter().all(|x| x == 0).then_some(result)
897}
898
899#[test]
900fn unit_product_length_conversion_test() {
901 use Option::None;
902 use Unit::*;
903 assert_eq!(
904 unit_product_length_conversion(&[(Px, 1)], &[(Phx, 1)]),
905 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
906 );
907 assert_eq!(
908 unit_product_length_conversion(&[(Phx, -2)], &[(Px, -2)]),
909 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
910 );
911 assert_eq!(
912 unit_product_length_conversion(&[(Px, 1), (Phx, -2)], &[(Phx, -1)]),
913 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -1 })
914 );
915 assert_eq!(
916 unit_product_length_conversion(
917 &[(Deg, 3), (Phx, 2), (Ms, -1)],
918 &[(Phx, 4), (Deg, 3), (Ms, -1), (Px, -2)]
919 ),
920 Some(LengthConversionPowers { rem_to_px_power: 0, px_to_phx_power: -2 })
921 );
922 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
923 assert_eq!(unit_product_length_conversion(&[(Deg, 1), (Phx, -2)], &[(Px, -2)]), None);
924 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
925
926 assert_eq!(
927 unit_product_length_conversion(&[(Rem, 1)], &[(Px, 1)]),
928 Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: 0 })
929 );
930 assert_eq!(
931 unit_product_length_conversion(&[(Rem, 1)], &[(Phx, 1)]),
932 Some(LengthConversionPowers { rem_to_px_power: -1, px_to_phx_power: -1 })
933 );
934 assert_eq!(
935 unit_product_length_conversion(&[(Rem, 2)], &[(Phx, 2)]),
936 Some(LengthConversionPowers { rem_to_px_power: -2, px_to_phx_power: -2 })
937 );
938}