microcad_lang/model/
mod.rs

1// Copyright © 2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Model tree module
5
6pub mod attribute;
7pub mod builder;
8pub mod creator;
9pub mod element;
10mod inner;
11pub mod iter;
12pub mod models;
13pub mod operation;
14pub mod output_type;
15pub mod properties;
16pub mod workpiece;
17
18pub use attribute::*;
19pub use builder::*;
20pub use creator::*;
21pub use element::*;
22pub use inner::*;
23pub use iter::*;
24pub use models::*;
25pub use operation::*;
26pub use output_type::*;
27pub use properties::*;
28pub use workpiece::*;
29
30use derive_more::{Deref, DerefMut};
31
32use microcad_core::BooleanOp;
33
34use crate::{
35    diag::WriteToFile, rc::RcMut, src_ref::SrcReferrer, syntax::Identifier, tree_display::*,
36    value::Value,
37};
38
39/// A reference counted, mutable [`Model`].
40#[derive(Clone, Deref, DerefMut)]
41pub struct Model(RcMut<ModelInner>);
42
43impl Model {
44    /// Create new model from inner.
45    pub fn new(inner: RcMut<ModelInner>) -> Self {
46        Self(inner)
47    }
48
49    /// Calculate depth of the model.
50    pub fn depth(&self) -> usize {
51        self.parents().count()
52    }
53
54    /// Check if a model contains an operation element.
55    pub fn is_operation(&self) -> bool {
56        self.borrow().element.is_operation()
57    }
58
59    /// Return `true`, if model has no children.
60    pub fn is_empty(&self) -> bool {
61        self.borrow().is_empty()
62    }
63
64    /// Return `true`, if model wont produce any output
65    pub fn is_empty_model(&self) -> bool {
66        let self_ = self.borrow();
67        match self_.element.value {
68            Element::BuiltinWorkpiece(_) | Element::InputPlaceholder => false,
69            _ => self_.is_empty(),
70        }
71    }
72
73    /// Make a deep copy if this model.
74    /// TODO: isn't this a Clone?
75    pub fn make_deep_copy(&self) -> Self {
76        let copy = Self(RcMut::new(self.0.borrow().clone_content()));
77        for child in self.borrow().children.iter() {
78            copy.append(child.make_deep_copy());
79        }
80        copy
81    }
82
83    /// Return address of this model.
84    pub fn addr(&self) -> usize {
85        self.0.as_ptr().addr()
86    }
87
88    /// Check if `other` is and `self` have the same address.
89    pub fn is_same_as(&self, other: &Model) -> bool {
90        self.addr() == other.addr()
91    }
92
93    /// Remove child from this model.
94    pub fn remove_child(&self, child: &Model) {
95        let mut s = self.0.borrow_mut();
96        s.children.retain(|model| !model.is_same_as(child));
97    }
98
99    /// Detaches a model from its parent. Children are not affected.
100    pub fn detach(&self) {
101        match self.0.borrow_mut().parent {
102            Some(ref mut parent) => {
103                parent.remove_child(self);
104            }
105            None => return,
106        }
107
108        self.0.borrow_mut().parent = None;
109    }
110
111    /// Append a single model as child.
112    ///
113    /// Also tries to set the output type if it has not been determined yet.
114    pub fn append(&self, model: Model) -> Model {
115        model.borrow_mut().parent = Some(self.clone());
116
117        let mut self_ = self.0.borrow_mut();
118        self_.children.push(model.clone());
119
120        model
121    }
122
123    /// Append multiple models as children.
124    ///
125    /// Return self.
126    pub fn append_children(&self, models: Models) -> Self {
127        for model in models.iter() {
128            self.append(model.clone());
129        }
130        self.clone()
131    }
132
133    /// Short cut to generate boolean operator as binary operation with two models.
134    pub fn boolean_op(self, op: BooleanOp, other: Model) -> Model {
135        assert!(self != other, "lhs and rhs must be distinct.");
136        Models::from(vec![self.clone(), other]).boolean_op(op)
137    }
138
139    /// Replace each input placeholder with copies of `input_model`.
140    pub fn replace_input_placeholders(&self, input_model: &Model) -> Self {
141        self.descendants().for_each(|model| {
142            let mut model_ = model.borrow_mut();
143            if model_.id.is_none() && matches!(model_.element.value, Element::InputPlaceholder) {
144                let mut input_model_ = input_model.borrow_mut();
145                input_model_.parent = Some(self.clone());
146                *model_ = input_model_.clone_content();
147                model_.children = input_model_.children.clone();
148            }
149        });
150        self.clone()
151    }
152
153    /// Deduce output type from children and set it and return it.
154    pub fn deduce_output_type(&self) -> OutputType {
155        let self_ = self.borrow();
156        let mut output_type = self_.element.output_type();
157        if output_type == OutputType::NotDetermined {
158            let children = &self_.children;
159            output_type = children.deduce_output_type();
160        }
161
162        output_type
163    }
164
165    /// Return inner group if this model only contains a group as single child.
166    ///
167    /// This function is used when we evaluate operations like `subtract() {}` or `hull() {}`.
168    /// When evaluating these operations, we want to iterate over the group's children.
169    pub fn into_group(&self) -> Option<Model> {
170        let children = &self.borrow().children;
171        if children.len() != 1 {
172            return None;
173        }
174
175        children.first().and_then(|n| {
176            if let Element::Group = *n.0.borrow().element {
177                Some(n.clone())
178            } else {
179                None
180            }
181        })
182    }
183}
184
185/// Iterator methods.
186impl Model {
187    /// Returns an iterator of models to this model and its unnamed descendants, in tree order.
188    ///
189    /// Includes the current model.
190    pub fn descendants(&self) -> Descendants {
191        Descendants::new(self.clone())
192    }
193
194    /// Returns an iterator of models that belong to the same source file as this one
195    pub fn source_file_descendants(&self) -> SourceFileDescendants {
196        SourceFileDescendants::new(self.clone())
197    }
198
199    /// Parents iterator.
200    pub fn parents(&self) -> Parents {
201        Parents::new(self.clone())
202    }
203
204    /// Ancestors iterator.
205    pub fn ancestors(&self) -> Ancestors {
206        Ancestors::new(self.clone())
207    }
208
209    /// Get a property from this model.
210    pub fn get_property(&self, id: &Identifier) -> Option<Value> {
211        self.borrow().element.get_property(id).cloned()
212    }
213
214    /// Set a property in this model.
215    pub fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
216        self.borrow_mut().element.set_property(id, value)
217    }
218
219    /// Add a new property to the model.
220    pub fn add_property(&self, id: Identifier, value: Value) {
221        self.borrow_mut()
222            .element
223            .add_properties([(id, value)].into_iter().collect())
224    }
225}
226
227impl AttributesAccess for Model {
228    fn get_attributes_by_id(&self, id: &Identifier) -> Vec<Attribute> {
229        self.borrow().attributes.get_attributes_by_id(id)
230    }
231}
232
233impl PartialEq for Model {
234    fn eq(&self, other: &Self) -> bool {
235        self.addr() == other.addr()
236    }
237}
238
239impl SrcReferrer for Model {
240    fn src_ref(&self) -> crate::src_ref::SrcRef {
241        self.borrow().src_ref()
242    }
243}
244
245impl std::fmt::Display for Model {
246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247        write!(
248            f,
249            "{id}{element}{is_root} ->",
250            id = match &self.borrow().id {
251                Some(id) => format!("{id}: "),
252                None => String::new(),
253            },
254            element = *self.borrow().element,
255            is_root = if self.parents().next().is_some() {
256                ""
257            } else {
258                " (root)"
259            }
260        )
261    }
262}
263
264impl std::fmt::Debug for Model {
265    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266        write!(
267            f,
268            "{}",
269            crate::shorten!(format!(
270                "{id}{element}{is_root} ->",
271                id = match &self.borrow().id {
272                    Some(id) => format!("{id:?}: "),
273                    None => String::new(),
274                },
275                element = *self.borrow().element,
276                is_root = if self.parents().next().is_some() {
277                    ""
278                } else {
279                    " (root)"
280                }
281            ))
282        )
283    }
284}
285
286impl TreeDisplay for Model {
287    fn tree_print(
288        &self,
289        f: &mut std::fmt::Formatter,
290        mut tree_state: TreeState,
291    ) -> std::fmt::Result {
292        let signature = if tree_state.debug {
293            format!("{self:?}")
294        } else {
295            self.to_string()
296        };
297        let self_ = self.borrow();
298        if let Some(output) = &self_.output {
299            writeln!(f, "{:tree_state$}{signature} {output}", "",)?;
300        } else {
301            writeln!(f, "{:tree_state$}{signature}", "",)?;
302        }
303        tree_state.indent();
304        if let Some(props) = self_.get_properties() {
305            props.tree_print(f, tree_state)?;
306        }
307        self_.attributes.tree_print(f, tree_state)?;
308        self_.children.tree_print(f, tree_state)
309    }
310}
311
312impl WriteToFile for Model {}