Skip to main content

kcl_api/
cad_op.rs

1use indexmap::IndexMap;
2use kcl_error::SourceRange;
3use schemars::JsonSchema;
4use serde::Deserialize;
5use serde::Serialize;
6
7use super::ArtifactId;
8use crate::ModuleId;
9use crate::NumericType;
10use crate::ast::ItemVisibility;
11use crate::ast::node_path::NodePath;
12use crate::front::ObjectId;
13
14/// A CAD modeling operation for display in the feature tree, AKA operations
15/// timeline.
16#[allow(clippy::large_enum_variant)]
17#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema, Deserialize)]
18#[ts(export_to = "Operation.ts")]
19#[serde(tag = "type")]
20pub enum Operation {
21    #[serde(rename_all = "camelCase")]
22    StdLibCall {
23        name: String,
24        /// The unlabeled argument to the function.
25        unlabeled_arg: Option<OpArg>,
26        /// The labeled keyword arguments to the function.
27        labeled_args: IndexMap<String, OpArg>,
28        /// The node path of the operation in the source code.
29        node_path: NodePath,
30        /// The true source range of the operation in the source code.
31        source_range: SourceRange,
32        /// The source range that's the boundary of calling the standard
33        /// library.
34        #[serde(default, skip_serializing_if = "Option::is_none")]
35        stdlib_entry_source_range: Option<SourceRange>,
36        /// True if the operation resulted in an error.
37        #[serde(default, skip_serializing_if = "is_false")]
38        is_error: bool,
39    },
40    #[serde(rename_all = "camelCase")]
41    VariableDeclaration {
42        /// The variable name.
43        name: String,
44        /// The value of the variable.
45        value: OpKclValue,
46        /// The visibility modifier of the variable, e.g. `export`.  `Default`
47        /// means there is no visibility modifier.
48        visibility: ItemVisibility,
49        /// The node path of the operation in the source code.
50        node_path: NodePath,
51        /// The source range of the operation in the source code.
52        source_range: SourceRange,
53    },
54    #[serde(rename_all = "camelCase")]
55    GroupBegin {
56        /// The details of the group.
57        group: Group,
58        /// The node path of the operation in the source code.
59        node_path: NodePath,
60        /// The source range of the operation in the source code.
61        source_range: SourceRange,
62    },
63    #[serde(rename_all = "camelCase")]
64    ModuleInstance {
65        /// The name of the module being used.
66        name: String,
67        /// The ID of the module which can be used to determine its path.
68        module_id: ModuleId,
69        /// Whether this is a glob import (`import * from "foo.kcl"`).
70        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
71        glob: bool,
72        /// The node path of the operation in the source code.
73        node_path: NodePath,
74        /// The source range of the operation in the source code.
75        source_range: SourceRange,
76    },
77    GroupEnd,
78}
79
80impl Operation {
81    /// If the variant is `StdLibCall`, set the `is_error` field.
82    pub fn set_std_lib_call_is_error(&mut self, is_err: bool) {
83        match self {
84            Self::StdLibCall { is_error, .. } => *is_error = is_err,
85            Self::VariableDeclaration { .. }
86            | Self::GroupBegin { .. }
87            | Self::ModuleInstance { .. }
88            | Self::GroupEnd => {}
89        }
90    }
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
94#[ts(export_to = "Operation.ts")]
95#[serde(tag = "type")]
96#[cfg_attr(not(target_arch = "wasm32"), expect(clippy::large_enum_variant))]
97pub enum Group {
98    /// A function call.
99    #[serde(rename_all = "camelCase")]
100    FunctionCall {
101        /// The name of the user-defined function being called.  Anonymous
102        /// functions have no name.
103        name: Option<String>,
104        /// The location of the function being called so that there's enough
105        /// info to go to its definition.
106        function_source_range: SourceRange,
107        /// The unlabeled argument to the function.
108        unlabeled_arg: Option<OpArg>,
109        /// The labeled keyword arguments to the function.
110        labeled_args: IndexMap<String, OpArg>,
111    },
112    /// A sketch block.
113    #[allow(dead_code)]
114    #[serde(rename_all = "camelCase")]
115    SketchBlock {
116        /// The ID of the sketch this group wraps.
117        sketch_id: ObjectId,
118    },
119}
120
121/// An argument to a CAD modeling operation.
122#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
123#[ts(export_to = "Operation.ts")]
124#[serde(rename_all = "camelCase")]
125pub struct OpArg {
126    /// The runtime value of the argument.  Instead of using [`KclValue`], we
127    /// refer to scene objects using their [`ArtifactId`]s.
128    value: OpKclValue,
129    /// The KCL code expression for the argument.  This is used in the UI so
130    /// that the user can edit the expression.
131    source_range: SourceRange,
132}
133
134impl OpArg {
135    pub fn new(value: OpKclValue, source_range: SourceRange) -> Self {
136        Self { value, source_range }
137    }
138
139    pub fn value(&self) -> &OpKclValue {
140        &self.value
141    }
142}
143
144fn is_false(b: &bool) -> bool {
145    !*b
146}
147
148/// A KCL value used in Operations.  `ArtifactId`s are used to refer to the
149/// actual scene objects.  Any data not needed in the UI may be omitted.
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
151#[ts(export_to = "Operation.ts")]
152#[serde(tag = "type")]
153pub enum OpKclValue {
154    Uuid {
155        value: ::uuid::Uuid,
156    },
157    Bool {
158        value: bool,
159    },
160    Number {
161        value: f64,
162        ty: NumericType,
163    },
164    String {
165        value: String,
166    },
167    /// Shown in the feature tree by nominal identity, e.g. `Color::Red`, not by
168    /// the variant's representation.
169    Enum {
170        enum_name: String,
171        variant: String,
172    },
173    SketchVar {
174        value: f64,
175        ty: NumericType,
176    },
177    Array {
178        value: Vec<OpKclValue>,
179    },
180    Object {
181        value: OpKclObjectFields,
182    },
183    TagIdentifier {
184        /// The name of the tag identifier.
185        value: String,
186        /// The artifact ID of the object it refers to.
187        artifact_id: Option<ArtifactId>,
188    },
189    TagDeclarator {
190        name: String,
191    },
192    GdtAnnotation {
193        artifact_id: ArtifactId,
194    },
195    /// A camera view argument. Fieldless because a camera registers no
196    /// artifact: the marker only separates a passed camera from an omitted one.
197    CameraView {},
198    Plane {
199        artifact_id: ArtifactId,
200    },
201    Face {
202        artifact_id: ArtifactId,
203    },
204    Sketch {
205        value: Box<OpSketch>,
206    },
207    Segment {
208        artifact_id: ArtifactId,
209    },
210    Solid {
211        value: Box<OpSolid>,
212    },
213    Helix {
214        value: Box<OpHelix>,
215    },
216    ImportedGeometry {
217        artifact_id: ArtifactId,
218    },
219    Function {},
220    Module {},
221    Type {},
222    KclNone {},
223    BoundedEdge {},
224}
225
226pub type OpKclObjectFields = IndexMap<String, OpKclValue>;
227
228#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS, Deserialize, JsonSchema)]
229#[ts(export_to = "Operation.ts")]
230#[serde(rename_all = "camelCase")]
231pub struct OpSketch {
232    artifact_id: ArtifactId,
233}
234
235impl OpSketch {
236    pub fn new(artifact_id: ArtifactId) -> Self {
237        Self { artifact_id }
238    }
239
240    pub fn artifact_id(&self) -> ArtifactId {
241        self.artifact_id
242    }
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
246#[ts(export_to = "Operation.ts")]
247#[serde(rename_all = "camelCase")]
248pub struct OpSolid {
249    artifact_id: ArtifactId,
250}
251
252impl OpSolid {
253    pub fn new(artifact_id: ArtifactId) -> Self {
254        Self { artifact_id }
255    }
256
257    pub fn artifact_id(&self) -> ArtifactId {
258        self.artifact_id
259    }
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
263#[ts(export_to = "Operation.ts")]
264#[serde(rename_all = "camelCase")]
265pub struct OpHelix {
266    artifact_id: ArtifactId,
267}
268
269impl OpHelix {
270    pub fn new(artifact_id: ArtifactId) -> Self {
271        Self { artifact_id }
272    }
273
274    pub fn artifact_id(&self) -> ArtifactId {
275        self.artifact_id
276    }
277}