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, 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    /// Fetch all values as `Vec<Value>`
35    pub fn fetch(&self) -> Vec<Value> {
36        self.items.iter().cloned().collect::<Vec<_>>()
37    }
38}
39
40impl PartialEq for Array {
41    fn eq(&self, other: &Self) -> bool {
42        self.ty == other.ty && self.items == other.items
43    }
44}
45
46impl IntoIterator for Array {
47    type Item = Value;
48    type IntoIter = std::vec::IntoIter<Self::Item>;
49
50    fn into_iter(self) -> Self::IntoIter {
51        self.items.into_iter()
52    }
53}
54
55impl TryFrom<ValueList> for Array {
56    type Error = ValueError;
57    fn try_from(items: ValueList) -> ValueResult<Array> {
58        match items.types().common_type() {
59            Some(ty) => Ok(Array::from_values(items, ty)),
60            None => Err(ValueError::CommonTypeExpected),
61        }
62    }
63}
64
65impl FromIterator<Value> for Array {
66    fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
67        let items: ValueList = iter.into_iter().collect();
68        let ty = items.types().common_type().expect("Common type");
69        Self { ty, items }
70    }
71}
72
73impl std::fmt::Display for Array {
74    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
75        write!(
76            f,
77            "[{items}]",
78            items = self
79                .items
80                .iter()
81                .map(|v| v.to_string())
82                .collect::<Vec<_>>()
83                .join(", ")
84        )
85    }
86}
87
88impl std::fmt::Debug for Array {
89    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90        write!(
91            f,
92            "[{items}]",
93            items = self
94                .items
95                .iter()
96                .map(|v| format!("{v:?}"))
97                .collect::<Vec<_>>()
98                .join(", ")
99        )
100    }
101}
102
103impl crate::ty::Ty for Array {
104    fn ty(&self) -> Type {
105        Type::Array(Box::new(self.ty.clone()))
106    }
107}
108
109/// + operator. Adds a value to an array, e.g.: `[1,2] + 1 == [2,3]`.
110impl std::ops::Add<Value> for Array {
111    type Output = ValueResult;
112
113    fn add(self, rhs: Value) -> Self::Output {
114        if self.ty.is_compatible_to(&rhs.ty()) {
115            Ok(Value::Array(Self::from_values(
116                ValueList::new(
117                    self.items
118                        .iter()
119                        .map(|value| value.clone() + rhs.clone())
120                        .collect::<Result<Vec<_>, _>>()?,
121                ),
122                self.ty,
123            )))
124        } else {
125            Err(ValueError::InvalidOperator("+".into()))
126        }
127    }
128}
129
130/// - operator. Subtracts a value from an array, e.g.: `[1,2] - 1 == [0,1]`.
131impl std::ops::Sub<Value> for Array {
132    type Output = ValueResult;
133
134    fn sub(self, rhs: Value) -> Self::Output {
135        if self.ty.is_compatible_to(&rhs.ty()) {
136            Ok(Value::Array(Self::from_values(
137                ValueList::new(
138                    self.items
139                        .iter()
140                        .map(|value| value.clone() - rhs.clone())
141                        .collect::<Result<Vec<_>, _>>()?,
142                ),
143                self.ty,
144            )))
145        } else {
146            Err(ValueError::InvalidOperator("-".into()))
147        }
148    }
149}
150
151/// * operator. Multiply a value from an array, e.g.: `[1,2] * 2 == [2,4]`.
152impl std::ops::Mul<Value> for Array {
153    type Output = ValueResult;
154
155    fn mul(self, rhs: Value) -> Self::Output {
156        match self.ty {
157            // List * Scalar or List * Integer
158            Type::Quantity(_) | Type::Integer => Ok(Value::Array(Array::from_values(
159                ValueList::new({
160                    self.iter()
161                        .map(|value| value.clone() * rhs.clone())
162                        .collect::<Result<Vec<_>, _>>()?
163                }),
164                self.ty * rhs.ty().clone(),
165            ))),
166            _ => Err(ValueError::InvalidOperator("*".into())),
167        }
168    }
169}
170
171/// / operator. Divide an array by value, e.g.: `[2,4] / 2 == [1,2]`.
172impl std::ops::Div<Value> for Array {
173    type Output = ValueResult;
174
175    fn div(self, rhs: Value) -> Self::Output {
176        let values = ValueList::new(
177            self.iter()
178                .map(|value| value.clone() / rhs.clone())
179                .collect::<Result<Vec<_>, _>>()?,
180        );
181
182        match (&self.ty, rhs.ty()) {
183            // Integer / Integer => Scalar
184            (Type::Integer, Type::Integer) => Ok(Value::Array(Array::from_values(
185                values,
186                self.ty / rhs.ty().clone(),
187            ))),
188            (Type::Quantity(_), _) => Ok(Value::Array(values.try_into()?)),
189            _ => Err(ValueError::InvalidOperator("/".into())),
190        }
191    }
192}
193
194impl std::ops::Neg for Array {
195    type Output = ValueResult;
196
197    fn neg(self) -> Self::Output {
198        let items: ValueList = self
199            .iter()
200            .map(|value| -value.clone())
201            .collect::<Result<Vec<_>, _>>()?
202            .into_iter()
203            .collect();
204        Ok(Value::Array(items.try_into()?))
205    }
206}
207
208#[test]
209fn test_array_debug() {
210    let val1 = Value::Target(Target::new("my::name1".into(), Some("my::target1".into())));
211    let val2 = Value::Target(Target::new("my::name2".into(), None));
212
213    let mut array = Array::new(Type::Target);
214    array.push(val1);
215    array.push(val2);
216
217    log::info!("{array}");
218    log::info!("{array:?}");
219}