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, Debug, 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 crate::ty::Ty for Array {
154    fn ty(&self) -> Type {
155        Type::Array(Box::new(self.ty.clone()))
156    }
157}
158
159/// + operator. Adds a value to an array, e.g.: `[1,2] + 1 == [2,3]`.
160impl std::ops::Add<Value> for Array {
161    type Output = ValueResult;
162
163    fn add(self, rhs: Value) -> Self::Output {
164        if self.ty.is_compatible_to(&rhs.ty()) {
165            Ok(Value::Array(Self::from_values(
166                ValueList::new(
167                    self.items
168                        .iter()
169                        .map(|value| value.clone() + rhs.clone())
170                        .collect::<Result<Vec<_>, _>>()?,
171                ),
172                self.ty,
173            )))
174        } else {
175            Err(ValueError::InvalidOperator("+".into()))
176        }
177    }
178}
179
180/// - operator. Subtracts a value from an array, e.g.: `[1,2] - 1 == [0,1]`.
181impl std::ops::Sub<Value> for Array {
182    type Output = ValueResult;
183
184    fn sub(self, rhs: Value) -> Self::Output {
185        if self.ty.is_compatible_to(&rhs.ty()) {
186            Ok(Value::Array(Self::from_values(
187                ValueList::new(
188                    self.items
189                        .iter()
190                        .map(|value| value.clone() - rhs.clone())
191                        .collect::<Result<Vec<_>, _>>()?,
192                ),
193                self.ty,
194            )))
195        } else {
196            Err(ValueError::InvalidOperator("-".into()))
197        }
198    }
199}
200
201/// * operator. Multiply a value from an array, e.g.: `[1,2] * 2 == [2,4]`.
202impl std::ops::Mul<Value> for Array {
203    type Output = ValueResult;
204
205    fn mul(self, rhs: Value) -> Self::Output {
206        match self.ty {
207            // List * Scalar or List * Integer
208            Type::Quantity(_) | Type::Integer => Ok(Value::Array(Array::from_values(
209                ValueList::new({
210                    self.iter()
211                        .map(|value| value.clone() * rhs.clone())
212                        .collect::<Result<Vec<_>, _>>()?
213                }),
214                self.ty * rhs.ty().clone(),
215            ))),
216            _ => Err(ValueError::InvalidOperator("*".into())),
217        }
218    }
219}
220
221/// / operator. Divide an array by value, e.g.: `[2,4] / 2 == [1,2]`.
222impl std::ops::Div<Value> for Array {
223    type Output = ValueResult;
224
225    fn div(self, rhs: Value) -> Self::Output {
226        let values = ValueList::new(
227            self.iter()
228                .map(|value| value.clone() / rhs.clone())
229                .collect::<Result<Vec<_>, _>>()?,
230        );
231
232        match (&self.ty, rhs.ty()) {
233            // Integer / Integer => Scalar
234            (Type::Integer, Type::Integer) => Ok(Value::Array(Array::from_values(
235                values,
236                self.ty / rhs.ty().clone(),
237            ))),
238            (Type::Quantity(_), _) => Ok(Value::Array(values.try_into()?)),
239            _ => Err(ValueError::InvalidOperator("/".into())),
240        }
241    }
242}
243
244impl std::ops::Neg for Array {
245    type Output = ValueResult;
246
247    fn neg(self) -> Self::Output {
248        let items = ValueList::new(
249            self.iter()
250                .map(|value| -value.clone())
251                .collect::<Result<Vec<_>, _>>()?,
252        );
253        Ok(Value::Array(items.try_into()?))
254    }
255}
256
257impl std::ops::Not for Array {
258    type Output = ValueResult;
259
260    fn not(self) -> Self::Output {
261        let items = ValueList::new(
262            self.iter()
263                .map(|value| !value.clone())
264                .collect::<Result<Vec<_>, _>>()?,
265        );
266        Ok(Value::Array(items.try_into()?))
267    }
268}