1use indexmap::IndexMap;
2use serde::Serialize;
3
4use super::ArtifactId;
5use super::KclValue;
6use super::types::NumericType;
7use crate::ModuleId;
8use crate::NodePath;
9use crate::SourceRange;
10use crate::front::ObjectId;
11use crate::parsing::ast::types::ItemVisibility;
12
13#[allow(clippy::large_enum_variant)]
16#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
17#[ts(export_to = "Operation.ts")]
18#[serde(tag = "type")]
19pub enum Operation {
20 #[serde(rename_all = "camelCase")]
21 StdLibCall {
22 name: String,
23 unlabeled_arg: Option<OpArg>,
25 labeled_args: IndexMap<String, OpArg>,
27 node_path: NodePath,
29 source_range: SourceRange,
31 #[serde(default, skip_serializing_if = "Option::is_none")]
34 stdlib_entry_source_range: Option<SourceRange>,
35 #[serde(default, skip_serializing_if = "is_false")]
37 is_error: bool,
38 },
39 #[serde(rename_all = "camelCase")]
40 VariableDeclaration {
41 name: String,
43 value: OpKclValue,
45 visibility: ItemVisibility,
48 node_path: NodePath,
50 source_range: SourceRange,
52 },
53 #[serde(rename_all = "camelCase")]
54 GroupBegin {
55 group: Group,
57 node_path: NodePath,
59 source_range: SourceRange,
61 },
62 GroupEnd,
63 #[allow(dead_code)]
64 #[serde(rename_all = "camelCase")]
65 SketchSolve {
66 sketch_id: ObjectId,
68 node_path: NodePath,
70 source_range: SourceRange,
72 },
73}
74
75impl Operation {
76 pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
78 match self {
79 Self::StdLibCall { is_error, .. } => *is_error = is_err,
80 Self::VariableDeclaration { .. } | Self::GroupBegin { .. } | Self::GroupEnd | Self::SketchSolve { .. } => {}
81 }
82 }
83
84 #[cfg(feature = "artifact-graph")]
85 pub(crate) fn fill_node_paths(&mut self, programs: &crate::execution::ProgramLookup, cached_body_items: usize) {
86 match self {
87 Operation::StdLibCall {
88 node_path,
89 source_range,
90 stdlib_entry_source_range,
91 ..
92 } => {
93 let range = stdlib_entry_source_range.as_ref().unwrap_or(source_range);
98 node_path.fill_placeholder(programs, cached_body_items, *range);
99 }
100 Operation::VariableDeclaration {
101 node_path,
102 source_range,
103 ..
104 }
105 | Operation::GroupBegin {
106 node_path,
107 source_range,
108 ..
109 } => {
110 node_path.fill_placeholder(programs, cached_body_items, *source_range);
111 }
112 Operation::GroupEnd => {}
113 Operation::SketchSolve {
114 node_path,
115 source_range,
116 ..
117 } => {
118 node_path.fill_placeholder(programs, cached_body_items, *source_range);
119 }
120 }
121 }
122}
123
124#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
125#[ts(export_to = "Operation.ts")]
126#[serde(tag = "type")]
127#[expect(clippy::large_enum_variant)]
128pub enum Group {
129 #[serde(rename_all = "camelCase")]
131 FunctionCall {
132 name: Option<String>,
135 function_source_range: SourceRange,
138 unlabeled_arg: Option<OpArg>,
140 labeled_args: IndexMap<String, OpArg>,
142 },
143 #[allow(dead_code)]
145 #[serde(rename_all = "camelCase")]
146 ModuleInstance {
147 name: String,
149 module_id: ModuleId,
151 },
152}
153
154#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
156#[ts(export_to = "Operation.ts")]
157#[serde(rename_all = "camelCase")]
158pub struct OpArg {
159 value: OpKclValue,
162 source_range: SourceRange,
165}
166
167impl OpArg {
168 pub(crate) fn new(value: OpKclValue, source_range: SourceRange) -> Self {
169 Self { value, source_range }
170 }
171}
172
173fn is_false(b: &bool) -> bool {
174 !*b
175}
176
177#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
180#[ts(export_to = "Operation.ts")]
181#[serde(tag = "type")]
182pub enum OpKclValue {
183 Uuid {
184 value: ::uuid::Uuid,
185 },
186 Bool {
187 value: bool,
188 },
189 Number {
190 value: f64,
191 ty: NumericType,
192 },
193 String {
194 value: String,
195 },
196 SketchVar {
197 value: f64,
198 ty: NumericType,
199 },
200 Array {
201 value: Vec<OpKclValue>,
202 },
203 Object {
204 value: OpKclObjectFields,
205 },
206 TagIdentifier {
207 value: String,
209 artifact_id: Option<ArtifactId>,
211 },
212 TagDeclarator {
213 name: String,
214 },
215 GdtAnnotation {
216 artifact_id: ArtifactId,
217 },
218 Plane {
219 artifact_id: ArtifactId,
220 },
221 Face {
222 artifact_id: ArtifactId,
223 },
224 Sketch {
225 value: Box<OpSketch>,
226 },
227 Segment {
228 artifact_id: ArtifactId,
229 },
230 Solid {
231 value: Box<OpSolid>,
232 },
233 Helix {
234 value: Box<OpHelix>,
235 },
236 ImportedGeometry {
237 artifact_id: ArtifactId,
238 },
239 Function {},
240 Module {},
241 Type {},
242 KclNone {},
243 BoundedEdge {},
244}
245
246pub type OpKclObjectFields = IndexMap<String, OpKclValue>;
247
248#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
249#[ts(export_to = "Operation.ts")]
250#[serde(rename_all = "camelCase")]
251pub struct OpSketch {
252 artifact_id: ArtifactId,
253}
254
255#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
256#[ts(export_to = "Operation.ts")]
257#[serde(rename_all = "camelCase")]
258pub struct OpSolid {
259 artifact_id: ArtifactId,
260}
261
262#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
263#[ts(export_to = "Operation.ts")]
264#[serde(rename_all = "camelCase")]
265pub struct OpHelix {
266 artifact_id: ArtifactId,
267}
268
269impl From<&KclValue> for OpKclValue {
270 fn from(value: &KclValue) -> Self {
271 match value {
272 KclValue::Uuid { value, .. } => Self::Uuid { value: *value },
273 KclValue::Bool { value, .. } => Self::Bool { value: *value },
274 KclValue::Number { value, ty, .. } => Self::Number { value: *value, ty: *ty },
275 KclValue::String { value, .. } => Self::String { value: value.clone() },
276 KclValue::SketchVar { value, .. } => Self::SketchVar {
277 value: value.initial_value,
278 ty: value.ty,
279 },
280 KclValue::SketchConstraint { .. } => {
281 debug_assert!(false, "Sketch constraint cannot be represented in operations");
282 Self::KclNone {}
283 }
284 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
285 let value = value.iter().map(Self::from).collect();
286 Self::Array { value }
287 }
288 KclValue::Object { value, .. } => {
289 let value = value.iter().map(|(k, v)| (k.clone(), Self::from(v))).collect();
290 Self::Object { value }
291 }
292 KclValue::TagIdentifier(tag_identifier) => Self::TagIdentifier {
293 value: tag_identifier.value.clone(),
294 artifact_id: tag_identifier.get_cur_info().map(|info| ArtifactId::new(info.id)),
295 },
296 KclValue::TagDeclarator(node) => Self::TagDeclarator {
297 name: node.name.clone(),
298 },
299 KclValue::GdtAnnotation { value } => Self::GdtAnnotation {
300 artifact_id: ArtifactId::new(value.id),
301 },
302 KclValue::Plane { value } => Self::Plane {
303 artifact_id: value.artifact_id,
304 },
305 KclValue::Face { value } => Self::Face {
306 artifact_id: value.artifact_id,
307 },
308 KclValue::Segment { value } => match &value.repr {
309 crate::execution::geometry::SegmentRepr::Unsolved { .. } => {
310 Self::KclNone {}
312 }
313 crate::execution::geometry::SegmentRepr::Solved { segment, .. } => Self::Segment {
314 artifact_id: ArtifactId::new(segment.id),
315 },
316 },
317 KclValue::Sketch { value } => Self::Sketch {
318 value: Box::new(OpSketch {
319 artifact_id: value.artifact_id,
320 }),
321 },
322 KclValue::Solid { value } => Self::Solid {
323 value: Box::new(OpSolid {
324 artifact_id: value.artifact_id,
325 }),
326 },
327 KclValue::Helix { value } => Self::Helix {
328 value: Box::new(OpHelix {
329 artifact_id: value.artifact_id,
330 }),
331 },
332 KclValue::ImportedGeometry(imported_geometry) => Self::ImportedGeometry {
333 artifact_id: ArtifactId::new(imported_geometry.id),
334 },
335 KclValue::Function { .. } => Self::Function {},
336 KclValue::Module { .. } => Self::Module {},
337 KclValue::KclNone { .. } => Self::KclNone {},
338 KclValue::Type { .. } => Self::Type {},
339 KclValue::BoundedEdge { .. } => Self::BoundedEdge {},
340 }
341 }
342}