1use std::borrow::Cow;
5use std::collections::{BTreeMap, HashMap, HashSet};
6use std::fmt::Display;
7use std::rc::Rc;
8
9use itertools::Itertools;
10
11use crate::expression_tree::{Expression, Unit};
12use crate::object_tree::Component;
13use crate::parser::syntax_nodes;
14use crate::typeregister::TypeRegister;
15
16#[derive(Debug, Clone)]
17pub enum Type {
18 Invalid,
20 Void,
22 InferredProperty,
24 InferredCallback,
26 Component(Rc<Component>),
27 Builtin(Rc<BuiltinElement>),
28 Native(Rc<NativeClass>),
29
30 Callback {
31 return_type: Option<Box<Type>>,
32 args: Vec<Type>,
33 },
34 Function {
35 return_type: Box<Type>,
36 args: Vec<Type>,
37 },
38
39 Float32,
41 Int32,
42 String,
43 Color,
44 Duration,
45 PhysicalLength,
46 LogicalLength,
47 Angle,
48 Percent,
49 Image,
50 Bool,
51 Model,
52 PathData, Easing,
54 Brush,
55 Array(Box<Type>),
57 Struct {
58 fields: BTreeMap<String, Type>,
59 name: Option<String>,
62 node: Option<syntax_nodes::ObjectType>,
64 },
65 Enumeration(Rc<Enumeration>),
66
67 UnitProduct(Vec<(Unit, i8)>),
71
72 ElementReference,
73
74 LayoutCache,
76}
77
78impl core::cmp::PartialEq for Type {
79 fn eq(&self, other: &Self) -> bool {
80 match self {
81 Type::Invalid => matches!(other, Type::Invalid),
82 Type::Void => matches!(other, Type::Void),
83 Type::InferredProperty => matches!(other, Type::InferredProperty),
84 Type::InferredCallback => matches!(other, Type::InferredCallback),
85 Type::Component(a) => matches!(other, Type::Component(b) if Rc::ptr_eq(a, b)),
86 Type::Builtin(a) => matches!(other, Type::Builtin(b) if Rc::ptr_eq(a, b)),
87 Type::Native(a) => matches!(other, Type::Native(b) if Rc::ptr_eq(a, b)),
88 Type::Callback { args: a, return_type: ra } => {
89 matches!(other, Type::Callback { args: b, return_type: rb } if a == b && ra == rb)
90 }
91 Type::Function { return_type: lhs_rt, args: lhs_args } => {
92 matches!(other, Type::Function { return_type: rhs_rt, args: rhs_args } if lhs_rt == rhs_rt && lhs_args == rhs_args)
93 }
94 Type::Float32 => matches!(other, Type::Float32),
95 Type::Int32 => matches!(other, Type::Int32),
96 Type::String => matches!(other, Type::String),
97 Type::Color => matches!(other, Type::Color),
98 Type::Duration => matches!(other, Type::Duration),
99 Type::Angle => matches!(other, Type::Angle),
100 Type::PhysicalLength => matches!(other, Type::PhysicalLength),
101 Type::LogicalLength => matches!(other, Type::LogicalLength),
102 Type::Percent => matches!(other, Type::Percent),
103 Type::Image => matches!(other, Type::Image),
104 Type::Bool => matches!(other, Type::Bool),
105 Type::Model => matches!(other, Type::Model),
106 Type::PathData => matches!(other, Type::PathData),
107 Type::Easing => matches!(other, Type::Easing),
108 Type::Brush => matches!(other, Type::Brush),
109 Type::Array(a) => matches!(other, Type::Array(b) if a == b),
110 Type::Struct { fields, name, node: _ } => {
111 matches!(other, Type::Struct{fields: f, name: n, node: _} if fields == f && name == n)
112 }
113 Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
114 Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
115 Type::ElementReference => matches!(other, Type::ElementReference),
116 Type::LayoutCache => matches!(other, Type::LayoutCache),
117 }
118 }
119}
120
121impl Display for Type {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 match self {
124 Type::Invalid => write!(f, "<error>"),
125 Type::Void => write!(f, "void"),
126 Type::InferredProperty => write!(f, "?"),
127 Type::InferredCallback => write!(f, "callback"),
128 Type::Component(c) => c.id.fmt(f),
129 Type::Builtin(b) => b.name.fmt(f),
130 Type::Native(b) => b.class_name.fmt(f),
131 Type::Callback { args, return_type } => {
132 write!(f, "callback")?;
133 if !args.is_empty() {
134 write!(f, "(")?;
135 for (i, arg) in args.iter().enumerate() {
136 if i > 0 {
137 write!(f, ",")?;
138 }
139 write!(f, "{}", arg)?;
140 }
141 write!(f, ")")?
142 }
143 if let Some(rt) = return_type {
144 write!(f, "-> {}", rt)?;
145 }
146 Ok(())
147 }
148 Type::Function { return_type, args } => {
149 write!(f, "function(")?;
150 for (i, arg) in args.iter().enumerate() {
151 if i > 0 {
152 write!(f, ",")?;
153 }
154 write!(f, "{}", arg)?;
155 }
156 write!(f, ") -> {}", return_type)
157 }
158 Type::Float32 => write!(f, "float"),
159 Type::Int32 => write!(f, "int"),
160 Type::String => write!(f, "string"),
161 Type::Duration => write!(f, "duration"),
162 Type::Angle => write!(f, "angle"),
163 Type::PhysicalLength => write!(f, "physical-length"),
164 Type::LogicalLength => write!(f, "length"),
165 Type::Percent => write!(f, "percent"),
166 Type::Color => write!(f, "color"),
167 Type::Image => write!(f, "image"),
168 Type::Bool => write!(f, "bool"),
169 Type::Model => write!(f, "model"),
170 Type::Array(t) => write!(f, "[{}]", t),
171 Type::Struct { name: Some(name), .. } => write!(f, "{}", name),
172 Type::Struct { fields, name: None, .. } => {
173 write!(f, "{{ ")?;
174 for (k, v) in fields {
175 write!(f, "{}: {},", k, v)?;
176 }
177 write!(f, "}}")
178 }
179
180 Type::PathData => write!(f, "pathdata"),
181 Type::Easing => write!(f, "easing"),
182 Type::Brush => write!(f, "brush"),
183 Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
184 Type::UnitProduct(vec) => {
185 const POWERS: &[char] = &['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
186 let mut x = vec.iter().map(|(unit, power)| {
187 if *power == 1 {
188 return unit.to_string();
189 }
190 let mut res = format!("{}{}", unit, if *power < 0 { "⁻" } else { "" });
191 let value = power.abs().to_string();
192 for x in value.as_bytes() {
193 res.push(POWERS[(x - b'0') as usize]);
194 }
195
196 res
197 });
198 write!(f, "({})", x.join("×"))
199 }
200 Type::ElementReference => write!(f, "element ref"),
201 Type::LayoutCache => write!(f, "layout cache"),
202 }
203 }
204}
205
206impl Type {
207 pub fn is_property_type(&self) -> bool {
209 matches!(
210 self,
211 Self::Float32
212 | Self::Int32
213 | Self::String
214 | Self::Color
215 | Self::Duration
216 | Self::Angle
217 | Self::PhysicalLength
218 | Self::LogicalLength
219 | Self::Percent
220 | Self::Image
221 | Self::Bool
222 | Self::Model
223 | Self::Easing
224 | Self::Enumeration(_)
225 | Self::ElementReference
226 | Self::Struct { .. }
227 | Self::Array(_)
228 | Self::Brush
229 | Self::InferredProperty
230 )
231 }
232
233 pub fn ok_for_public_api(&self) -> bool {
234 !matches!(self, Self::Easing)
235 }
236
237 pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
238 match self {
239 Type::Component(c) => c.root_element.borrow().lookup_property(name),
240 Type::Builtin(b) => {
241 let resolved_name =
242 if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) {
243 Cow::Owned(alias_name.to_string())
244 } else {
245 Cow::Borrowed(name)
246 };
247 match b.properties.get(resolved_name.as_ref()) {
248 None => {
249 if b.is_non_item_type {
250 PropertyLookupResult { resolved_name, property_type: Type::Invalid }
251 } else {
252 crate::typeregister::reserved_property(name)
253 }
254 }
255 Some(p) => PropertyLookupResult { resolved_name, property_type: p.ty.clone() },
256 }
257 }
258 Type::Native(n) => {
259 let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) {
260 Cow::Owned(alias_name.to_string())
261 } else {
262 Cow::Borrowed(name)
263 };
264 let property_type =
265 n.lookup_property(resolved_name.as_ref()).cloned().unwrap_or_default();
266 PropertyLookupResult { resolved_name, property_type }
267 }
268 _ => PropertyLookupResult {
269 resolved_name: Cow::Borrowed(name),
270 property_type: Type::Invalid,
271 },
272 }
273 }
274
275 pub fn property_list(&self) -> Vec<(String, Type)> {
277 match self {
278 Type::Component(c) => {
279 let mut r = c.root_element.borrow().base_type.property_list();
280 r.extend(
281 c.root_element
282 .borrow()
283 .property_declarations
284 .iter()
285 .map(|(k, d)| (k.clone(), d.property_type.clone())),
286 );
287 r
288 }
289 Type::Builtin(b) => {
290 b.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
291 }
292 Type::Native(n) => {
293 n.properties.iter().map(|(k, t)| (k.clone(), t.ty.clone())).collect()
294 }
295 _ => Vec::new(),
296 }
297 }
298
299 pub fn lookup_type_for_child_element(
300 &self,
301 name: &str,
302 tr: &TypeRegister,
303 ) -> Result<Type, String> {
304 match self {
305 Type::Component(component) => {
306 return component
307 .root_element
308 .borrow()
309 .base_type
310 .lookup_type_for_child_element(name, tr)
311 }
312 Type::Builtin(builtin) => {
313 if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
314 return Ok(child_type.clone());
315 }
316 if builtin.disallow_global_types_as_child_elements {
317 let mut valid_children: Vec<_> =
318 builtin.additional_accepted_child_types.keys().cloned().collect();
319 valid_children.sort();
320
321 return Err(format!(
322 "{} is not allowed within {}. Only {} are valid children",
323 name,
324 builtin.native_class.class_name,
325 valid_children.join(" ")
326 ));
327 }
328 }
329 _ => {}
330 };
331 tr.lookup_element(name).and_then(|t| {
332 if !tr.expose_internal_types && matches!(&t, Type::Builtin(e) if e.is_internal) {
333 Err(format!("Unknown type {}. (The type exist as an internal type, but cannot be accessed in this scope)", name))
334 } else {
335 Ok(t)
336 }
337 })
338 }
339
340 pub fn lookup_member_function(&self, name: &str) -> Expression {
341 match self {
342 Type::Builtin(builtin) => builtin
343 .member_functions
344 .get(name)
345 .cloned()
346 .unwrap_or_else(|| crate::typeregister::reserved_member_function(name)),
347 Type::Component(component) => {
348 component.root_element.borrow().base_type.lookup_member_function(name)
349 }
350 _ => Expression::Invalid,
351 }
352 }
353
354 pub fn as_builtin(&self) -> &BuiltinElement {
356 match self {
357 Type::Builtin(b) => b,
358 Type::Component(_) => panic!("This should not happen because of inlining"),
359 _ => panic!("invalid type"),
360 }
361 }
362
363 pub fn as_native(&self) -> &NativeClass {
365 match self {
366 Type::Native(b) => b,
367 Type::Component(_) => {
368 panic!("This should not happen because of native class resolution")
369 }
370 _ => panic!("invalid type"),
371 }
372 }
373
374 pub fn as_component(&self) -> &Rc<Component> {
376 match self {
377 Type::Component(c) => c,
378 _ => panic!("should be a component because of the repeater_component pass"),
379 }
380 }
381
382 pub fn as_enum(&self) -> &Rc<Enumeration> {
384 match self {
385 Type::Enumeration(e) => e,
386 _ => panic!("should be an enumeration, bug in compiler pass"),
387 }
388 }
389
390 pub fn can_convert(&self, other: &Self) -> bool {
392 let can_convert_struct = |a: &BTreeMap<String, Type>, b: &BTreeMap<String, Type>| {
393 let mut has_more_property = false;
395 for (k, v) in b {
396 match a.get(k) {
397 Some(t) if !t.can_convert(v) => return false,
398 None => has_more_property = true,
399 _ => (),
400 }
401 }
402 if has_more_property {
403 if a.keys().any(|k| !b.contains_key(k)) {
405 return false;
406 }
407 }
408 true
409 };
410 match (self, other) {
411 (a, b) if a == b => true,
412 (_, Type::Invalid)
413 | (_, Type::Void)
414 | (Type::Float32, Type::Int32)
415 | (Type::Float32, Type::String)
416 | (Type::Int32, Type::Float32)
417 | (Type::Int32, Type::String)
418 | (Type::Array(_), Type::Model)
419 | (Type::Float32, Type::Model)
420 | (Type::Int32, Type::Model)
421 | (Type::PhysicalLength, Type::LogicalLength)
422 | (Type::LogicalLength, Type::PhysicalLength)
423 | (Type::Percent, Type::Float32)
424 | (Type::Brush, Type::Color)
425 | (Type::Color, Type::Brush) => true,
426 (Type::Struct { fields: a, .. }, Type::Struct { fields: b, .. }) => {
427 can_convert_struct(a, b)
428 }
429 (Type::UnitProduct(u), o) => match o.as_unit_product() {
430 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
431 None => false,
432 },
433 (o, Type::UnitProduct(u)) => match o.as_unit_product() {
434 Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
435 None => false,
436 },
437 _ => false,
438 }
439 }
440
441 pub fn collect_contextual_types(
442 &self,
443 context_restricted_types: &mut HashMap<String, HashSet<String>>,
444 ) {
445 let builtin = match self {
446 Type::Builtin(ty) => ty,
447 _ => return,
448 };
449 for (accepted_child_type_name, accepted_child_type) in
450 builtin.additional_accepted_child_types.iter()
451 {
452 context_restricted_types
453 .entry(accepted_child_type_name.clone())
454 .or_default()
455 .insert(builtin.native_class.class_name.clone());
456
457 accepted_child_type.collect_contextual_types(context_restricted_types);
458 }
459 }
460
461 pub fn default_unit(&self) -> Option<Unit> {
464 match self {
465 Type::Duration => Some(Unit::Ms),
466 Type::PhysicalLength => Some(Unit::Phx),
467 Type::LogicalLength => Some(Unit::Px),
468 Type::Percent => None,
470 Type::Angle => Some(Unit::Deg),
471 Type::Invalid => None,
472 Type::Void => None,
473 Type::InferredProperty | Type::InferredCallback => None,
474 Type::Component(_) => None,
475 Type::Builtin(_) => None,
476 Type::Native(_) => None,
477 Type::Callback { .. } => None,
478 Type::Function { .. } => None,
479 Type::Float32 => None,
480 Type::Int32 => None,
481 Type::String => None,
482 Type::Color => None,
483 Type::Image => None,
484 Type::Bool => None,
485 Type::Model => None,
486 Type::PathData => None,
487 Type::Easing => None,
488 Type::Brush => None,
489 Type::Array(_) => None,
490 Type::Struct { .. } => None,
491 Type::Enumeration(_) => None,
492 Type::UnitProduct(_) => None,
493 Type::ElementReference => None,
494 Type::LayoutCache => None,
495 }
496 }
497
498 pub fn as_unit_product(&self) -> Option<Vec<(Unit, i8)>> {
500 match self {
501 Type::UnitProduct(u) => Some(u.clone()),
502 Type::Float32 | Type::Int32 => Some(Vec::new()),
503 _ => self.default_unit().map(|u| vec![(u, 1)]),
504 }
505 }
506}
507
508impl Default for Type {
509 fn default() -> Self {
510 Self::Invalid
511 }
512}
513
514#[derive(Debug, Clone)]
516pub struct BuiltinPropertyInfo {
517 pub ty: Type,
519 pub default_value: Option<Expression>,
521 pub is_native_output: bool,
526}
527
528impl BuiltinPropertyInfo {
529 pub fn new(ty: Type) -> Self {
530 Self { ty, default_value: None, is_native_output: false }
531 }
532}
533
534#[derive(Debug, Clone, Default)]
535pub struct NativeClass {
536 pub parent: Option<Rc<NativeClass>>,
537 pub class_name: String,
538 pub cpp_vtable_getter: String,
539 pub properties: HashMap<String, BuiltinPropertyInfo>,
540 pub deprecated_aliases: HashMap<String, String>,
541 pub cpp_type: Option<String>,
542 pub rust_type_constructor: Option<String>,
543}
544
545impl NativeClass {
546 pub fn new(class_name: &str) -> Self {
547 let cpp_vtable_getter = format!("SIXTYFPS_GET_ITEM_VTABLE({}VTable)", class_name);
548 Self {
549 class_name: class_name.into(),
550 cpp_vtable_getter,
551 properties: Default::default(),
552 ..Default::default()
553 }
554 }
555
556 pub fn new_with_properties(
557 class_name: &str,
558 properties: impl IntoIterator<Item = (String, BuiltinPropertyInfo)>,
559 ) -> Self {
560 let mut class = Self::new(class_name);
561 class.properties = properties.into_iter().collect();
562 class
563 }
564
565 pub fn property_count(&self) -> usize {
566 self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
567 }
568
569 pub fn lookup_property(&self, name: &str) -> Option<&Type> {
570 if let Some(bty) = self.properties.get(name) {
571 Some(&bty.ty)
572 } else if let Some(parent_class) = &self.parent {
573 parent_class.lookup_property(name)
574 } else {
575 None
576 }
577 }
578
579 pub fn lookup_alias(&self, name: &str) -> Option<&str> {
580 if let Some(alias_target) = self.deprecated_aliases.get(name) {
581 Some(alias_target)
582 } else if self.properties.contains_key(name) {
583 None
584 } else if let Some(parent_class) = &self.parent {
585 parent_class.lookup_alias(name)
586 } else {
587 None
588 }
589 }
590}
591
592#[derive(Debug, Clone, Copy)]
593pub enum DefaultSizeBinding {
594 None,
596 ExpandsToParentGeometry,
598 ImplicitSize,
600}
601
602impl Default for DefaultSizeBinding {
603 fn default() -> Self {
604 Self::None
605 }
606}
607
608#[derive(Debug, Clone, Default)]
609pub struct BuiltinElement {
610 pub name: String,
611 pub native_class: Rc<NativeClass>,
612 pub properties: HashMap<String, BuiltinPropertyInfo>,
613 pub additional_accepted_child_types: HashMap<String, Type>,
614 pub disallow_global_types_as_child_elements: bool,
615 pub is_non_item_type: bool,
617 pub accepts_focus: bool,
618 pub member_functions: HashMap<String, Expression>,
619 pub is_global: bool,
620 pub default_size_binding: DefaultSizeBinding,
621 pub is_internal: bool,
623}
624
625impl BuiltinElement {
626 pub fn new(native_class: Rc<NativeClass>) -> Self {
627 Self { name: native_class.class_name.clone(), native_class, ..Default::default() }
628 }
629}
630
631#[derive(PartialEq, Debug)]
632pub struct PropertyLookupResult<'a> {
633 pub resolved_name: std::borrow::Cow<'a, str>,
634 pub property_type: Type,
635}
636
637impl<'a> PropertyLookupResult<'a> {
638 pub fn is_valid(&self) -> bool {
639 self.property_type != Type::Invalid
640 }
641}
642
643#[derive(Debug, Clone)]
644pub struct Enumeration {
645 pub name: String,
646 pub values: Vec<String>,
647 pub default_value: usize, }
649
650impl PartialEq for Enumeration {
651 fn eq(&self, other: &Self) -> bool {
652 self.name.eq(&other.name)
653 }
654}
655
656impl Enumeration {
657 pub fn default_value(self: Rc<Self>) -> EnumerationValue {
658 EnumerationValue { value: self.default_value, enumeration: self.clone() }
659 }
660
661 pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
662 self.values.iter().enumerate().find_map(|(idx, name)| {
663 if name == value {
664 Some(EnumerationValue { value: idx, enumeration: self.clone() })
665 } else {
666 None
667 }
668 })
669 }
670}
671
672#[derive(Clone, Debug)]
673pub struct EnumerationValue {
674 pub value: usize, pub enumeration: Rc<Enumeration>,
676}
677
678impl PartialEq for EnumerationValue {
679 fn eq(&self, other: &Self) -> bool {
680 Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
681 }
682}
683
684impl std::fmt::Display for EnumerationValue {
685 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 self.enumeration.values[self.value].fmt(f)
687 }
688}
689
690pub fn unit_product_length_conversion(a: &[(Unit, i8)], b: &[(Unit, i8)]) -> Option<i8> {
693 let mut it1 = a.iter();
694 let mut it2 = b.iter();
695 let (mut v1, mut v2) = (it1.next(), it2.next());
696 let mut ppx = 0;
697 let mut lpx = 0;
698 loop {
699 match (v1, v2) {
700 (None, None) => return (ppx == -lpx && ppx != 0).then(|| ppx),
701 (Some(a), Some(b)) if a == b => (),
702 (Some((Unit::Phx, a)), Some((Unit::Phx, b))) => ppx += a - b,
703 (Some((Unit::Px, a)), Some((Unit::Px, b))) => lpx += a - b,
704 (Some((Unit::Phx, a)), _) => {
705 ppx += *a;
706 v1 = it1.next();
707 continue;
708 }
709 (_, Some((Unit::Phx, b))) => {
710 ppx += -b;
711 v2 = it2.next();
712 continue;
713 }
714 (Some((Unit::Px, a)), _) => {
715 lpx += *a;
716 v1 = it1.next();
717 continue;
718 }
719 (_, Some((Unit::Px, b))) => {
720 lpx += -b;
721 v2 = it2.next();
722 continue;
723 }
724 _ => return None,
725 };
726 v1 = it1.next();
727 v2 = it2.next();
728 }
729}
730
731#[test]
732fn unit_product_length_conversion_test() {
733 use Option::None;
734 use Unit::*;
735 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, 1)]), Some(-1));
736 assert_eq!(unit_product_length_conversion(&[(Phx, -2)], &[(Px, -2)]), Some(-2));
737 assert_eq!(unit_product_length_conversion(&[(Px, 1), (Phx, -2)], &[(Phx, -1)]), Some(-1));
738 assert_eq!(
739 unit_product_length_conversion(
740 &[(Deg, 3), (Phx, 2), (Ms, -1)],
741 &[(Phx, 4), (Deg, 3), (Ms, -1), (Px, -2)]
742 ),
743 Some(-2)
744 );
745 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
746 assert_eq!(unit_product_length_conversion(&[(Deg, 1), (Phx, -2)], &[(Px, -2)]), None);
747 assert_eq!(unit_product_length_conversion(&[(Px, 1)], &[(Phx, -1)]), None);
748}