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    /// Get output type of element.
35    pub fn output_type(&self) -> OutputType {
36        match self {
37            Element::Workpiece(workpiece) => match workpiece.kind {
38                WorkbenchKind::Sketch => OutputType::Geometry2D,
39                WorkbenchKind::Part => OutputType::Geometry3D,
40                WorkbenchKind::Operation => OutputType::NotDetermined,
41            },
42            Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
43                BuiltinWorkbenchKind::Primitive2D => OutputType::Geometry2D,
44                BuiltinWorkbenchKind::Primitive3D => OutputType::Geometry3D,
45                BuiltinWorkbenchKind::Transform | BuiltinWorkbenchKind::Operation => {
46                    builtin_workpiece.output_type
47                }
48            },
49            Element::Group | Element::Multiplicity | Element::InputPlaceholder => {
50                OutputType::NotDetermined
51            }
52        }
53    }
54
55    /// Check if an element is an operation.
56    pub fn is_operation(&self) -> bool {
57        match self {
58            Element::BuiltinWorkpiece(builtin_workpiece) => match builtin_workpiece.kind {
59                BuiltinWorkbenchKind::Primitive2D | BuiltinWorkbenchKind::Primitive3D => false,
60                BuiltinWorkbenchKind::Operation | BuiltinWorkbenchKind::Transform => true,
61            },
62            Element::Multiplicity | Element::Group | Element::InputPlaceholder => false,
63            Element::Workpiece(workpiece) => match workpiece.kind {
64                WorkbenchKind::Part | WorkbenchKind::Sketch => false,
65                WorkbenchKind::Operation => true,
66            },
67        }
68    }
69}
70
71impl std::fmt::Display for Element {
72    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73        let name: &'static str = self.into();
74        match &self {
75            Element::Workpiece(workpiece) => write!(f, "{workpiece}"),
76            Element::BuiltinWorkpiece(builtin_workpiece) => write!(f, "{builtin_workpiece}"),
77            _ => write!(f, "{name}"),
78        }
79    }
80}
81
82impl std::hash::Hash for Element {
83    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
84        match self {
85            Element::Group => std::mem::discriminant(&Element::Group).hash(state),
86            Element::Multiplicity => std::mem::discriminant(&Element::Multiplicity).hash(state),
87            Element::InputPlaceholder => {
88                std::mem::discriminant(&Element::InputPlaceholder).hash(state)
89            }
90            Element::Workpiece(workpiece) => workpiece.computed_hash().hash(state),
91            Element::BuiltinWorkpiece(builtin_workpiece) => {
92                builtin_workpiece.computed_hash().hash(state)
93            }
94        }
95    }
96}
97
98impl PropertiesAccess for Element {
99    fn get_property(&self, id: &Identifier) -> Option<&Value> {
100        match self {
101            Self::Workpiece(workpiece) => workpiece.get_property(id),
102            _ => unreachable!("not a workpiece element"),
103        }
104    }
105
106    fn set_property(&mut self, id: Identifier, value: Value) -> Option<Value> {
107        match self {
108            Self::Workpiece(workpiece) => workpiece.set_property(id, value),
109            _ => unreachable!("not a workpiece element"),
110        }
111    }
112
113    fn get_properties(&self) -> Option<&Properties> {
114        match self {
115            Self::Workpiece(workpiece) => workpiece.get_properties(),
116            _ => None,
117        }
118    }
119
120    fn add_properties(&mut self, props: Properties) {
121        match self {
122            Self::Workpiece(workpiece) => {
123                workpiece.add_properties(props);
124            }
125            _ => unreachable!("not a workpiece element"),
126        }
127    }
128}