microcad_lang/ty/
tuple_type.rs1use crate::ty::*;
7
8use microcad_lang_base::Identifier;
9
10#[derive(Debug, Clone, Default, PartialEq, Eq)]
12pub struct TupleType {
13 pub named: microcad_core::hash::HashMap<Identifier, Type>,
15 pub unnamed: microcad_core::hash::HashSet<Type>,
17}
18
19impl TupleType {
20 pub fn new_vec2() -> Self {
22 [("x", Type::scalar()), ("y", Type::scalar())]
23 .into_iter()
24 .collect()
25 }
26
27 pub fn new_vec3() -> Self {
29 [
30 ("x", Type::scalar()),
31 ("y", Type::scalar()),
32 ("z", Type::scalar()),
33 ]
34 .into_iter()
35 .collect()
36 }
37
38 pub fn new_color() -> Self {
40 [
41 ("r", Type::scalar()),
42 ("g", Type::scalar()),
43 ("b", Type::scalar()),
44 ("a", Type::scalar()),
45 ]
46 .into_iter()
47 .collect()
48 }
49
50 pub fn new_size2() -> Self {
52 [("width", Type::length()), ("height", Type::length())]
53 .into_iter()
54 .collect()
55 }
56
57 pub(crate) fn is_matching(&self, params: &TupleType) -> bool {
59 if self == params {
60 true
61 } else if self.unnamed.is_empty()
62 && params.unnamed.is_empty()
63 && self.named.len() == params.named.len()
64 {
65 self.named.iter().all(|arg| {
66 if let Some(ty) = params.named.get(arg.0) {
67 arg.1 == ty || arg.1.is_array_of(ty)
68 } else {
69 false
70 }
71 })
72 } else {
73 false
74 }
75 }
76
77 fn matches_keys(&self, keys: &[&str]) -> bool {
79 if !self.unnamed.is_empty() || self.named.len() != keys.len() {
80 return false;
81 }
82 keys.iter()
83 .all(|k| self.named.contains_key(&Identifier::no_ref(k)))
84 }
85
86 fn is_scalar_only(&self) -> bool {
88 self.common_type().is_some_and(|ty| *ty == Type::scalar())
89 }
90
91 fn is_length_only(&self) -> bool {
93 self.common_type().is_some_and(|ty| *ty == Type::length())
94 }
95
96 pub(crate) fn common_type(&self) -> Option<&Type> {
98 let mut iter = self.unnamed.iter().chain(self.named.values());
99 if let Some(first) = iter.next() {
100 if iter.all(|x| x == first) {
101 return Some(first);
102 }
103 }
104 None
105 }
106
107 pub(crate) fn is_color(&self) -> bool {
109 self.is_scalar_only() && self.matches_keys(&["r", "g", "b", "a"])
110 }
111
112 pub(crate) fn is_vec2(&self) -> bool {
114 self.is_scalar_only() && self.matches_keys(&["x", "y"])
115 }
116
117 pub(crate) fn is_vec3(&self) -> bool {
119 self.is_scalar_only() && self.matches_keys(&["x", "y", "z"])
120 }
121
122 pub(crate) fn is_size2(&self) -> bool {
124 self.is_length_only() && self.matches_keys(&["width", "height"])
125 }
126}
127
128impl std::hash::Hash for TupleType {
129 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130 self.named.iter().for_each(|(id, ty)| {
131 id.hash(state);
132 ty.hash(state)
133 });
134 self.unnamed.iter().for_each(|ty| ty.hash(state));
135 }
136}
137
138impl FromIterator<(Identifier, Type)> for TupleType {
139 fn from_iter<T: IntoIterator<Item = (Identifier, Type)>>(iter: T) -> Self {
140 let (unnamed, named) = iter.into_iter().partition(|(id, _)| id.is_empty());
141 Self {
142 named,
143 unnamed: unnamed.into_values().collect(),
144 }
145 }
146}
147
148impl<'a> FromIterator<(&'a str, Type)> for TupleType {
149 fn from_iter<T: IntoIterator<Item = (&'a str, Type)>>(iter: T) -> Self {
150 let (unnamed, named) = iter
151 .into_iter()
152 .map(|(id, ty)| (Identifier::no_ref(id), ty))
153 .partition(|(id, _)| id.is_empty());
154 Self {
155 named,
156 unnamed: unnamed.into_values().collect(),
157 }
158 }
159}
160
161impl std::fmt::Display for TupleType {
162 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
163 if self.is_color() {
164 return write!(f, "Color");
165 }
166 if self.is_vec2() {
167 return write!(f, "Vec2");
168 }
169 if self.is_vec3() {
170 return write!(f, "Vec3");
171 }
172 if self.is_size2() {
173 return write!(f, "Size2");
174 }
175
176 write!(f, "({})", {
177 let mut types = self
178 .named
179 .iter()
180 .map(|(id, ty)| format!("{id}: {ty}"))
181 .chain(self.unnamed.iter().map(|ty| ty.to_string()))
182 .collect::<Vec<_>>();
183
184 types.sort();
185 types.join(", ")
186 })
187 }
188}
189
190#[test]
191fn test_tuple_type_eq() {
192 assert_eq!(TupleType::new_color(), TupleType::new_color());
193}
194
195#[test]
196fn test_tuple_type_match() {
197 let args = TupleType {
198 named: [
199 (Identifier::no_ref("x"), Type::Integer),
200 (
201 Identifier::no_ref("y"),
202 Type::Array(Box::new(Type::Integer)),
203 ),
204 ]
205 .into_iter()
206 .collect(),
207 unnamed: Default::default(),
208 };
209 let params = TupleType {
210 named: [
211 (Identifier::no_ref("x"), Type::Integer),
212 (Identifier::no_ref("y"), Type::Integer),
213 ]
214 .into_iter()
215 .collect(),
216 unnamed: Default::default(),
217 };
218 assert!(args.is_matching(¶ms));
219}