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    /// Test if the named tuple has exactly all the given keys
53    fn matches(&self, keys: &[&str]) -> bool {
54        if !self.unnamed.is_empty() || self.named.len() != keys.len() {
55            return false;
56        }
57        keys.iter()
58            .all(|k| self.named.contains_key(&Identifier::no_ref(k)))
59    }
60
61    /// Checks if the named tuple type only holds scalar values.
62    fn is_scalar_only(&self) -> bool {
63        self.common_type().is_some_and(|ty| *ty == Type::scalar())
64    }
65
66    /// Checks if the named tuple type only holds length values.
67    fn is_length_only(&self) -> bool {
68        self.common_type().is_some_and(|ty| *ty == Type::length())
69    }
70
71    /// Test if all fields have a common type.
72    pub(crate) fn common_type(&self) -> Option<&Type> {
73        let mut iter = self.unnamed.iter().chain(self.named.values());
74        if let Some(first) = iter.next() {
75            if iter.all(|x| x == first) {
76                return Some(first);
77            }
78        }
79        None
80    }
81
82    /// Check if the named tuple is a [`Color`].
83    pub(crate) fn is_color(&self) -> bool {
84        self.is_scalar_only() && self.matches(&["r", "g", "b", "a"])
85    }
86
87    /// Check if the named tuple is a [`Vec2`].
88    pub(crate) fn is_vec2(&self) -> bool {
89        self.is_scalar_only() && self.matches(&["x", "y"])
90    }
91
92    /// Check if the named tuple is a [`Vec3`].
93    pub(crate) fn is_vec3(&self) -> bool {
94        self.is_scalar_only() && self.matches(&["x", "y", "z"])
95    }
96
97    /// Check if the named tuple is a [`Size2`]
98    pub(crate) fn is_size2(&self) -> bool {
99        self.is_length_only() && self.matches(&["width", "height"])
100    }
101}
102
103impl std::hash::Hash for TupleType {
104    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
105        self.named.iter().for_each(|(id, ty)| {
106            id.hash(state);
107            ty.hash(state)
108        });
109        self.unnamed.iter().for_each(|ty| ty.hash(state));
110    }
111}
112
113impl FromIterator<(Identifier, Type)> for TupleType {
114    fn from_iter<T: IntoIterator<Item = (Identifier, Type)>>(iter: T) -> Self {
115        let (unnamed, named) = iter.into_iter().partition(|(id, _)| id.is_empty());
116        Self {
117            named,
118            unnamed: unnamed.into_values().collect(),
119        }
120    }
121}
122
123impl<'a> FromIterator<(&'a str, Type)> for TupleType {
124    fn from_iter<T: IntoIterator<Item = (&'a str, Type)>>(iter: T) -> Self {
125        let (unnamed, named) = iter
126            .into_iter()
127            .map(|(id, ty)| (Identifier::no_ref(id), ty))
128            .partition(|(id, _)| id.is_empty());
129        Self {
130            named,
131            unnamed: unnamed.into_values().collect(),
132        }
133    }
134}
135
136impl std::fmt::Display for TupleType {
137    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
138        if self.is_color() {
139            return write!(f, "Color");
140        }
141        if self.is_vec2() {
142            return write!(f, "Vec2");
143        }
144        if self.is_vec3() {
145            return write!(f, "Vec3");
146        }
147        if self.is_size2() {
148            return write!(f, "Size2");
149        }
150
151        write!(f, "({})", {
152            let mut types = self
153                .named
154                .iter()
155                .map(|(id, ty)| format!("{id}: {ty}"))
156                .chain(self.unnamed.iter().map(|ty| ty.to_string()))
157                .collect::<Vec<_>>();
158
159            types.sort();
160            types.join(", ")
161        })
162    }
163}