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