Skip to main content

microcad_lang/model/
element.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Element of a [`Model`].
5
6use crate::{builtin::*, model::*, value::*};
7use strum::IntoStaticStr;
8
9/// An element defines the entity of a [`Model`].
10#[derive(Clone, IntoStaticStr, Debug, Default, derive_more::From)]
11pub enum Element {
12    #[default]
13    /// A group element is created by a body `{}`.
14    Group,
15
16    /// An element containing a value.
17    Value(Value),
18
19    /// A workpiece that holds properties.
20    ///
21    /// A workpiece is created by workbenches.
22    Workpiece(Workpiece),
23
24    /// A built-in workpiece.
25    ///
26    /// A workpiece is created by workbenches.
27    BuiltinWorkpiece(BuiltinWorkpiece),
28
29    /// Multiplicity.
30    Multiplicity,
31
32    /// Created from `@input` marker. Will never be part of the final model.
33    InputPlaceholder,
34}
35
36impl Element {
37    /// Creator.
38    pub fn creator(&self) -> Option<&Creator> {
39        match self {
40            Element::Workpiece(workpiece) => Some(&workpiece.creator),
41            Element::BuiltinWorkpiece(builtin_workpiece) => Some(&builtin_workpiece.creator),
42            _ => None,
43        }
44    }
45
46    /// Get output type of element.
47    pub fn output_type(&self) -> OutputType {
48        match self {
49            Element::Workpiece(workpiece) => workpiece.kind.into(),
50            Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
51                BuiltinWorkbenchKind::Primitive2D => OutputType::Geometry2D,
52                BuiltinWorkbenchKind::Primitive3D => OutputType::Geometry3D,
53                BuiltinWorkbenchKind::Transform | BuiltinWorkbenchKind::Operation => {
54                    builtin_workpiece.output_type
55                }
56            },
57            Element::Group
58            | Element::Multiplicity
59            | Element::InputPlaceholder
60            | Element::Value(_) => OutputType::NotDetermined,
61        }
62    }
63}
64
65impl std::fmt::Display for Element {
66    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
67        let name: &'static str = self.into();
68        match &self {
69            Element::Workpiece(workpiece) => write!(f, "{workpiece}"),
70            Element::BuiltinWorkpiece(builtin_workpiece) => write!(f, "{builtin_workpiece}"),
71            _ => write!(f, "{name}"),
72        }
73    }
74}
75
76impl std::hash::Hash for Element {
77    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
78        match self {
79            Element::Group => std::mem::discriminant(&Element::Group).hash(state),
80            Element::Multiplicity => std::mem::discriminant(&Element::Multiplicity).hash(state),
81            Element::InputPlaceholder => {
82                std::mem::discriminant(&Element::InputPlaceholder).hash(state)
83            }
84            Element::Workpiece(workpiece) => workpiece.computed_hash().hash(state),
85            Element::BuiltinWorkpiece(builtin_workpiece) => {
86                builtin_workpiece.computed_hash().hash(state)
87            }
88            Element::Value(value) => value.computed_hash().hash(state),
89        }
90    }
91}
92
93impl PropertiesAccess for Element {
94    fn get_property(&self, id: &Identifier) -> Option<&Value> {
95        match self {
96            Self::Workpiece(workpiece) => workpiece.get_property(id),
97            _ => unreachable!("not a workpiece element"),
98        }
99    }
100
101    fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
102        match self {
103            Self::Workpiece(workpiece) => workpiece.set_property(id, value),
104            _ => unreachable!("not a workpiece element"),
105        }
106    }
107
108    fn get_properties(&self) -> Option<&Properties> {
109        match self {
110            Self::Workpiece(workpiece) => workpiece.get_properties(),
111            _ => None,
112        }
113    }
114
115    fn add_properties(&mut self, props: Properties) {
116        match self {
117            Self::Workpiece(workpiece) => {
118                workpiece.add_properties(props);
119            }
120            _ => unreachable!("not a workpiece element"),
121        }
122    }
123}