microcad_lang/model/
element.rs

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