microcad_lang/value/
array.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.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(items: ValueList, ty: Type) -> Self {
23        Self { items, ty }
24    }
25
26    /// Fetch all values as `Vec<Value>`
27    pub fn fetch(&self) -> Vec<Value> {
28        self.items.iter().cloned().collect::<Vec<_>>()
29    }
30}
31
32impl PartialEq for Array {
33    fn eq(&self, other: &Self) -> bool {
34        self.ty == other.ty && self.items == other.items
35    }
36}
37
38impl IntoIterator for Array {
39    type Item = Value;
40    type IntoIter = std::vec::IntoIter<Self::Item>;
41
42    fn into_iter(self) -> Self::IntoIter {
43        self.items.into_iter()
44    }
45}
46
47impl TryFrom<ValueList> for Array {
48    type Error = ValueError;
49    fn try_from(items: ValueList) -> ValueResult<Array> {
50        match items.types().common_type() {
51            Some(ty) => Ok(Array::new(items, ty)),
52            None => Err(ValueError::CommonTypeExpected),
53        }
54    }
55}
56
57impl FromIterator<Value> for Array {
58    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
59        let items: ValueList = iter.into_iter().collect();
60        let ty = items.types().common_type().expect("Common type");
61        Self { ty, items }
62    }
63}
64
65impl std::fmt::Display for Array {
66    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
67        write!(
68            f,
69            "[{items}]",
70            items = self
71                .items
72                .iter()
73                .map(|v| v.to_string())
74                .collect::<Vec<_>>()
75                .join(", ")
76        )
77    }
78}
79
80impl crate::ty::Ty for Array {
81    fn ty(&self) -> Type {
82        Type::Array(Box::new(self.ty.clone()))
83    }
84}
85
86/// + operator. Adds a value to an array, e.g.: `[1,2] + 1 == [2,3]`.
87impl std::ops::Add<Value> for Array {
88    type Output = ValueResult;
89
90    fn add(self, rhs: Value) -> Self::Output {
91        if self.ty.is_add_compatible_to(&rhs.ty()) {
92            Ok(Value::Array(Self::new(
93                ValueList::new(
94                    self.items
95                        .iter()
96                        .map(|value| value.clone() + rhs.clone())
97                        .collect::<Result<Vec<_>, _>>()?,
98                ),
99                self.ty,
100            )))
101        } else {
102            Err(ValueError::InvalidOperator("+".into()))
103        }
104    }
105}
106
107/// - operator. Subtracts a value from an array, e.g.: `[1,2] - 1 == [0,1]`.
108impl std::ops::Sub<Value> for Array {
109    type Output = ValueResult;
110
111    fn sub(self, rhs: Value) -> Self::Output {
112        if self.ty.is_add_compatible_to(&rhs.ty()) {
113            Ok(Value::Array(Self::new(
114                ValueList::new(
115                    self.items
116                        .iter()
117                        .map(|value| value.clone() - rhs.clone())
118                        .collect::<Result<Vec<_>, _>>()?,
119                ),
120                self.ty,
121            )))
122        } else {
123            Err(ValueError::InvalidOperator("-".into()))
124        }
125    }
126}
127
128/// * operator. Multiply a value from an array, e.g.: `[1,2] * 2 == [2,4]`.
129impl std::ops::Mul<Value> for Array {
130    type Output = ValueResult;
131
132    fn mul(self, rhs: Value) -> Self::Output {
133        match self.ty {
134            // List * Scalar or List * Integer
135            Type::Quantity(_) | Type::Integer => Ok(Value::Array(Array::new(
136                ValueList::new({
137                    self.iter()
138                        .map(|value| value.clone() * rhs.clone())
139                        .collect::<Result<Vec<_>, _>>()?
140                }),
141                self.ty * rhs.ty().clone(),
142            ))),
143            _ => Err(ValueError::InvalidOperator("*".into())),
144        }
145    }
146}
147
148/// / operator. Divide an array by value, e.g.: `[2,4] / 2 == [1,2]`.
149impl std::ops::Div<Value> for Array {
150    type Output = ValueResult;
151
152    fn div(self, rhs: Value) -> Self::Output {
153        let values = ValueList::new(
154            self.iter()
155                .map(|value| value.clone() / rhs.clone())
156                .collect::<Result<Vec<_>, _>>()?,
157        );
158
159        match (&self.ty, rhs.ty()) {
160            // Integer / Integer => Scalar
161            (Type::Integer, Type::Integer) => Ok(Value::Array(Array::new(
162                values,
163                self.ty / rhs.ty().clone(),
164            ))),
165            (Type::Quantity(_), _) => Ok(Value::Array(values.try_into()?)),
166            _ => Err(ValueError::InvalidOperator("/".into())),
167        }
168    }
169}
170
171impl std::ops::Neg for Array {
172    type Output = ValueResult;
173
174    fn neg(self) -> Self::Output {
175        let items: ValueList = self
176            .iter()
177            .map(|value| -value.clone())
178            .collect::<Result<Vec<_>, _>>()?
179            .into_iter()
180            .collect();
181        Ok(Value::Array(items.try_into()?))
182    }
183}