microcad_lang/ty/
tuple_type.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Tuple type syntax element
5
6use crate::{syntax::*, ty::*};
7
8/// (Partially named) tuple (e.g. `(n: Scalar, m: String, Integer)`)
9#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct TupleType {
11    pub(crate) named: std::collections::HashMap<Identifier, Type>,
12    pub(crate) unnamed: std::collections::HashSet<Type>,
13}
14impl TupleType {
15    /// Create new Vec2 type.
16    pub fn new_vec2() -> Self {
17        [("x", Type::scalar()), ("y", Type::scalar())]
18            .into_iter()
19            .collect()
20    }
21
22    /// Create new Vec3 type.
23    pub fn new_vec3() -> Self {
24        [
25            ("x", Type::scalar()),
26            ("y", Type::scalar()),
27            ("z", Type::scalar()),
28        ]
29        .into_iter()
30        .collect()
31    }
32
33    /// Create new Color type.
34    pub fn new_color() -> Self {
35        [
36            ("r", Type::scalar()),
37            ("g", Type::scalar()),
38            ("b", Type::scalar()),
39            ("a", Type::scalar()),
40        ]
41        .into_iter()
42        .collect()
43    }
44
45    /// Create new Size2 type.
46    pub fn new_size2() -> Self {
47        [("width", Type::length()), ("height", Type::length())]
48            .into_iter()
49            .collect()
50    }
51
52    /// Match tuples by id.
53    ///
54    /// TODO: Does not check any inner types!
55    /// TODO: Use argument matching to match types.
56    pub fn matches(&self, other: &Self) -> bool {
57        if !self.unnamed.is_empty()
58            || self.named.len() != other.named.len()
59            || !self.unnamed.is_empty()
60            || !other.unnamed.is_empty()
61        {
62            return false;
63        }
64        other.named.iter().all(|k| self.named.contains_key(k.0))
65    }
66
67    /// Test if the named tuple has exactly all the given keys
68    fn matches_keys(&self, keys: &[&str]) -> bool {
69        if !self.unnamed.is_empty() || self.named.len() != keys.len() {
70            return false;
71        }
72        keys.iter()
73            .all(|k| self.named.contains_key(&Identifier::no_ref(k)))
74    }
75
76    /// Checks if the named tuple type only holds scalar values.
77    fn is_scalar_only(&self) -> bool {
78        self.common_type().is_some_and(|ty| *ty == Type::scalar())
79    }
80
81    /// Checks if the named tuple type only holds length values.
82    fn is_length_only(&self) -> bool {
83        self.common_type().is_some_and(|ty| *ty == Type::length())
84    }
85
86    /// Test if all fields have a common type.
87    pub(crate) fn common_type(&self) -> Option<&Type> {
88        let mut iter = self.unnamed.iter().chain(self.named.values());
89        if let Some(first) = iter.next() {
90            if iter.all(|x| x == first) {
91                return Some(first);
92            }
93        }
94        None
95    }
96
97    /// Check if the named tuple is a [`Color`].
98    pub(crate) fn is_color(&self) -> bool {
99        self.is_scalar_only() && self.matches_keys(&["r", "g", "b", "a"])
100    }
101
102    /// Check if the named tuple is a [`Vec2`].
103    pub(crate) fn is_vec2(&self) -> bool {
104        self.is_scalar_only() && self.matches_keys(&["x", "y"])
105    }
106
107    /// Check if the named tuple is a [`Vec3`].
108    pub(crate) fn is_vec3(&self) -> bool {
109        self.is_scalar_only() && self.matches_keys(&["x", "y", "z"])
110    }
111
112    /// Check if the named tuple is a [`Size2`]
113    pub(crate) fn is_size2(&self) -> bool {
114        self.is_length_only() && self.matches_keys(&["width", "height"])
115    }
116}
117
118impl std::hash::Hash for TupleType {
119    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
120        self.named.iter().for_each(|(id, ty)| {
121            id.hash(state);
122            ty.hash(state)
123        });
124        self.unnamed.iter().for_each(|ty| ty.hash(state));
125    }
126}
127
128impl FromIterator<(Identifier, Type)> for TupleType {
129    fn from_iter<T: IntoIterator<Item = (Identifier, Type)>>(iter: T) -> Self {
130        let (unnamed, named) = iter.into_iter().partition(|(id, _)| id.is_empty());
131        Self {
132            named,
133            unnamed: unnamed.into_values().collect(),
134        }
135    }
136}
137
138impl<'a> FromIterator<(&'a str, Type)> for TupleType {
139    fn from_iter<T: IntoIterator<Item = (&'a str, Type)>>(iter: T) -> Self {
140        let (unnamed, named) = iter
141            .into_iter()
142            .map(|(id, ty)| (Identifier::no_ref(id), ty))
143            .partition(|(id, _)| id.is_empty());
144        Self {
145            named,
146            unnamed: unnamed.into_values().collect(),
147        }
148    }
149}
150
151impl std::fmt::Display for TupleType {
152    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
153        if self.is_color() {
154            return write!(f, "Color");
155        }
156        if self.is_vec2() {
157            return write!(f, "Vec2");
158        }
159        if self.is_vec3() {
160            return write!(f, "Vec3");
161        }
162        if self.is_size2() {
163            return write!(f, "Size2");
164        }
165
166        write!(f, "({})", {
167            let mut types = self
168                .named
169                .iter()
170                .map(|(id, ty)| format!("{id}: {ty}"))
171                .chain(self.unnamed.iter().map(|ty| ty.to_string()))
172                .collect::<Vec<_>>();
173
174            types.sort();
175            types.join(", ")
176        })
177    }
178}