Skip to main content

microcad_lang/value/
array.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Typed list of values evaluation entity
5
6use crate::{ty::*, value::*};
7use derive_more::{Deref, DerefMut};
8
9/// Collection of values of the same type.
10#[derive(Clone, Deref, DerefMut)]
11pub struct Array {
12    /// List of values
13    #[deref]
14    #[deref_mut]
15    items: ValueList,
16    /// Element type.
17    ty: Type,
18}
19
20impl Array {
21    /// Create new list
22    pub fn new(ty: Type) -> Self {
23        Self {
24            items: ValueList::default(),
25            ty,
26        }
27    }
28
29    /// Create new list from `ValueList`.
30    pub fn from_values(items: ValueList, ty: Type) -> Self {
31        Self { items, ty }
32    }
33}
34
35/// All builtin methods and builtin functions.
36///
37/// `len` and `contains` are already implemented and accessible via `impl Deref`.
38impl Array {
39    /// Get the first element, or None
40    pub fn first(&self) -> Value {
41        self.items.first().cloned().unwrap_or_default()
42    }
43
44    /// Get the last element, or None
45    pub fn last(&self) -> Value {
46        self.items.last().cloned().unwrap_or_default()
47    }
48
49    /// Get all elements but the first
50    pub fn tail(&self) -> Array {
51        Array::from_values(
52            self.items.iter().skip(1).cloned().collect(),
53            self.ty.clone(),
54        )
55    }
56
57    /// Return a reversed version of the array.
58    pub fn rev(&self) -> Array {
59        Array::from_values(self.items.iter().rev().cloned().collect(), self.ty.clone())
60    }
61
62    /// Return a sorted version of this array.
63    ///
64    /// Only primitive types (quantities, integers, bools and string) can be sorted.
65    pub fn sorted(&self) -> Array {
66        let mut items = self.items.clone();
67        match self.ty {
68            Type::Integer | Type::Quantity(..) | Type::String | Type::Bool => {
69                items.sort_by(|a, b| {
70                    assert_eq!(a.ty(), b.ty());
71                    match (a, b) {
72                        (Value::Quantity(a), Value::Quantity(b)) => a.value.total_cmp(&b.value),
73                        (Value::Integer(a), Value::Integer(b)) => a.cmp(b),
74                        (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
75                        (Value::String(a), Value::String(b)) => a.cmp(b),
76                        _ => unreachable!(),
77                    }
78                })
79            }
80            _ => {}
81        };
82
83        Array::from_values(items, self.ty.clone())
84    }
85
86    /// Check if all items are equal.
87    pub fn all_equal(&self) -> bool {
88        match self.first() {
89            Value::None => true,
90            value => self[1..].iter().all(|x| *x == value),
91        }
92    }
93
94    /// Check if all items are sorted in ascending order.
95    pub fn is_ascending(&self) -> bool {
96        self.as_slice().windows(2).all(|w| w[0] <= w[1])
97    }
98
99    /// Check if all items are sorted in descending order.
100    pub fn is_descending(&self) -> bool {
101        self.as_slice().windows(2).all(|w| w[0] >= w[1])
102    }
103}
104
105impl PartialEq for Array {
106    fn eq(&self, other: &Self) -> bool {
107        self.ty == other.ty && self.items == other.items
108    }
109}
110
111impl IntoIterator for Array {
112    type Item = Value;
113    type IntoIter = std::vec::IntoIter<Self::Item>;
114
115    fn into_iter(self) -> Self::IntoIter {
116        self.items.into_iter()
117    }
118}
119
120impl TryFrom<ValueList> for Array {
121    type Error = ValueError;
122    fn try_from(items: ValueList) -> ValueResult<Array> {
123        match items.types().common_type() {
124            Some(ty) => Ok(Array::from_values(items, ty)),
125            None => Err(ValueError::CommonTypeExpected),
126        }
127    }
128}
129
130impl FromIterator<Value> for Array {
131    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
132        let items: ValueList = iter.into_iter().collect();
133        let ty = items.types().common_type().expect("Common type");
134        Self { ty, items }
135    }
136}
137
138impl std::fmt::Display for Array {
139    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
140        write!(
141            f,
142            "[{items}]",
143            items = self
144                .items
145                .iter()
146                .map(|v| v.to_string())
147                .collect::<Vec<_>>()
148                .join(", ")
149        )
150    }
151}
152
153impl std::fmt::Debug for Array {
154    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
155        write!(
156            f,
157            "[{items}]",
158            items = self
159                .items
160                .iter()
161                .map(|v| format!("{v:?}"))
162                .collect::<Vec<_>>()
163                .join(", ")
164        )
165    }
166}
167
168impl crate::ty::Ty for Array {
169    fn ty(&self) -> Type {
170        Type::Array(Box::new(self.ty.clone()))
171    }
172}
173
174/// + operator. Adds a value to an array, e.g.: `[1,2] + 1 == [2,3]`.
175impl std::ops::Add<Value> for Array {
176    type Output = ValueResult;
177
178    fn add(self, rhs: Value) -> Self::Output {
179        if self.ty.is_compatible_to(&rhs.ty()) {
180            Ok(Value::Array(Self::from_values(
181                ValueList::new(
182                    self.items
183                        .iter()
184                        .map(|value| value.clone() + rhs.clone())
185                        .collect::<Result<Vec<_>, _>>()?,
186                ),
187                self.ty,
188            )))
189        } else {
190            Err(ValueError::InvalidOperator("+".into()))
191        }
192    }
193}
194
195/// - operator. Subtracts a value from an array, e.g.: `[1,2] - 1 == [0,1]`.
196impl std::ops::Sub<Value> for Array {
197    type Output = ValueResult;
198
199    fn sub(self, rhs: Value) -> Self::Output {
200        if self.ty.is_compatible_to(&rhs.ty()) {
201            Ok(Value::Array(Self::from_values(
202                ValueList::new(
203                    self.items
204                        .iter()
205                        .map(|value| value.clone() - rhs.clone())
206                        .collect::<Result<Vec<_>, _>>()?,
207                ),
208                self.ty,
209            )))
210        } else {
211            Err(ValueError::InvalidOperator("-".into()))
212        }
213    }
214}
215
216/// * operator. Multiply a value from an array, e.g.: `[1,2] * 2 == [2,4]`.
217impl std::ops::Mul<Value> for Array {
218    type Output = ValueResult;
219
220    fn mul(self, rhs: Value) -> Self::Output {
221        match self.ty {
222            // List * Scalar or List * Integer
223            Type::Quantity(_) | Type::Integer => Ok(Value::Array(Array::from_values(
224                ValueList::new({
225                    self.iter()
226                        .map(|value| value.clone() * rhs.clone())
227                        .collect::<Result<Vec<_>, _>>()?
228                }),
229                self.ty * rhs.ty().clone(),
230            ))),
231            _ => Err(ValueError::InvalidOperator("*".into())),
232        }
233    }
234}
235
236/// / operator. Divide an array by value, e.g.: `[2,4] / 2 == [1,2]`.
237impl std::ops::Div<Value> for Array {
238    type Output = ValueResult;
239
240    fn div(self, rhs: Value) -> Self::Output {
241        let values = ValueList::new(
242            self.iter()
243                .map(|value| value.clone() / rhs.clone())
244                .collect::<Result<Vec<_>, _>>()?,
245        );
246
247        match (&self.ty, rhs.ty()) {
248            // Integer / Integer => Scalar
249            (Type::Integer, Type::Integer) => Ok(Value::Array(Array::from_values(
250                values,
251                self.ty / rhs.ty().clone(),
252            ))),
253            (Type::Quantity(_), _) => Ok(Value::Array(values.try_into()?)),
254            _ => Err(ValueError::InvalidOperator("/".into())),
255        }
256    }
257}
258
259impl std::ops::Neg for Array {
260    type Output = ValueResult;
261
262    fn neg(self) -> Self::Output {
263        let items = ValueList::new(
264            self.iter()
265                .map(|value| -value.clone())
266                .collect::<Result<Vec<_>, _>>()?,
267        );
268        Ok(Value::Array(items.try_into()?))
269    }
270}
271
272impl std::ops::Not for Array {
273    type Output = ValueResult;
274
275    fn not(self) -> Self::Output {
276        let items = ValueList::new(
277            self.iter()
278                .map(|value| !value.clone())
279                .collect::<Result<Vec<_>, _>>()?,
280        );
281        Ok(Value::Array(items.try_into()?))
282    }
283}