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 #[serde(rename_all = "camelCase")]
63 ModuleInstance {
64 name: String,
66 module_id: ModuleId,
68 node_path: NodePath,
70 source_range: SourceRange,
72 },
73 GroupEnd,
74}
75
76impl Operation {
77 pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
79 match self {
80 Self::StdLibCall { is_error, .. } => *is_error = is_err,
81 Self::VariableDeclaration { .. }
82 | Self::GroupBegin { .. }
83 | Self::ModuleInstance { .. }
84 | Self::GroupEnd => {}
85 }
86 }
87
88 pub(crate) fn fill_node_paths(&mut self, programs: &crate::execution::ProgramLookup, cached_body_items: usize) {
89 match self {
90 Operation::StdLibCall {
91 node_path,
92 source_range,
93 stdlib_entry_source_range,
94 ..
95 } => {
96 let range = stdlib_entry_source_range.as_ref().unwrap_or(source_range);
101 node_path.fill_placeholder(programs, cached_body_items, *range);
102 }
103 Operation::VariableDeclaration {
104 node_path,
105 source_range,
106 ..
107 }
108 | Operation::GroupBegin {
109 node_path,
110 source_range,
111 ..
112 }
113 | Operation::ModuleInstance {
114 node_path,
115 source_range,
116 ..
117 } => {
118 node_path.fill_placeholder(programs, cached_body_items, *source_range);
119 }
120 Operation::GroupEnd => {}
121 }
122 }
123}
124
125#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
126#[ts(export_to = "Operation.ts")]
127#[serde(tag = "type")]
128#[expect(clippy::large_enum_variant)]
129pub enum Group {
130 #[serde(rename_all = "camelCase")]
132 FunctionCall {
133 name: Option<String>,
136 function_source_range: SourceRange,
139 unlabeled_arg: Option<OpArg>,
141 labeled_args: IndexMap<String, OpArg>,
143 },
144 #[allow(dead_code)]
146 #[serde(rename_all = "camelCase")]
147 SketchBlock {
148 sketch_id: ObjectId,
150 },
151}
152
153#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
155#[ts(export_to = "Operation.ts")]
156#[serde(rename_all = "camelCase")]
157pub struct OpArg {
158 value: OpKclValue,
161 source_range: SourceRange,
164}
165
166impl OpArg {
167 pub(crate) fn new(value: OpKclValue, source_range: SourceRange) -> Self {
168 Self { value, source_range }
169 }
170}
171
172fn is_false(b: &bool) -> bool {
173 !*b
174}
175
176#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
179#[ts(export_to = "Operation.ts")]
180#[serde(tag = "type")]
181pub enum OpKclValue {
182 Uuid {
183 value: ::uuid::Uuid,
184 },
185 Bool {
186 value: bool,
187 },
188 Number {
189 value: f64,
190 ty: NumericType,
191 },
192 String {
193 value: String,
194 },
195 SketchVar {
196 value: f64,
197 ty: NumericType,
198 },
199 Array {
200 value: Vec<OpKclValue>,
201 },
202 Object {
203 value: OpKclObjectFields,
204 },
205 TagIdentifier {
206 value: String,
208 artifact_id: Option<ArtifactId>,
210 },
211 TagDeclarator {
212 name: String,
213 },
214 GdtAnnotation {
215 artifact_id: ArtifactId,
216 },
217 Plane {
218 artifact_id: ArtifactId,
219 },
220 Face {
221 artifact_id: ArtifactId,
222 },
223 Sketch {
224 value: Box<OpSketch>,
225 },
226 Segment {
227 artifact_id: ArtifactId,
228 },
229 Solid {
230 value: Box<OpSolid>,
231 },
232 Helix {
233 value: Box<OpHelix>,
234 },
235 ImportedGeometry {
236 artifact_id: ArtifactId,
237 },
238 Function {},
239 Module {},
240 Type {},
241 KclNone {},
242 BoundedEdge {},
243}
244
245pub type OpKclObjectFields = IndexMap<String, OpKclValue>;
246
247#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
248#[ts(export_to = "Operation.ts")]
249#[serde(rename_all = "camelCase")]
250pub struct OpSketch {
251 artifact_id: ArtifactId,
252}
253
254#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
255#[ts(export_to = "Operation.ts")]
256#[serde(rename_all = "camelCase")]
257pub struct OpSolid {
258 artifact_id: ArtifactId,
259}
260
261#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
262#[ts(export_to = "Operation.ts")]
263#[serde(rename_all = "camelCase")]
264pub struct OpHelix {
265 artifact_id: ArtifactId,
266}
267
268impl From<&KclValue> for OpKclValue {
269 fn from(value: &KclValue) -> Self {
270 match value {
271 KclValue::Uuid { value, .. } => Self::Uuid { value: *value },
272 KclValue::Bool { value, .. } => Self::Bool { value: *value },
273 KclValue::Number { value, ty, .. } => Self::Number { value: *value, ty: *ty },
274 KclValue::String { value, .. } => Self::String { value: value.clone() },
275 KclValue::SketchVar { value, .. } => Self::SketchVar {
276 value: value.initial_value,
277 ty: value.ty,
278 },
279 KclValue::SketchConstraint { .. } => {
280 debug_assert!(false, "Sketch constraint cannot be represented in operations");
281 Self::KclNone {}
282 }
283 KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
284 let value = value.iter().map(Self::from).collect();
285 Self::Array { value }
286 }
287 KclValue::Object { value, .. } => {
288 let value = value.iter().map(|(k, v)| (k.clone(), Self::from(v))).collect();
289 Self::Object { value }
290 }
291 KclValue::TagIdentifier(tag_identifier) => Self::TagIdentifier {
292 value: tag_identifier.value.clone(),
293 artifact_id: tag_identifier.get_cur_info().map(|info| ArtifactId::new(info.id)),
294 },
295 KclValue::TagDeclarator(node) => Self::TagDeclarator {
296 name: node.name.clone(),
297 },
298 KclValue::GdtAnnotation { value } => Self::GdtAnnotation {
299 artifact_id: ArtifactId::new(value.id),
300 },
301 KclValue::Plane { value } => Self::Plane {
302 artifact_id: value.artifact_id,
303 },
304 KclValue::Face { value } => Self::Face {
305 artifact_id: value.artifact_id,
306 },
307 KclValue::Segment { value } => match &value.repr {
308 crate::execution::geometry::SegmentRepr::Unsolved { .. } => {
309 Self::KclNone {}
311 }
312 crate::execution::geometry::SegmentRepr::Solved { segment, .. } => Self::Segment {
313 artifact_id: ArtifactId::new(segment.id),
314 },
315 },
316 KclValue::Sketch { value } => Self::Sketch {
317 value: Box::new(OpSketch {
318 artifact_id: value.artifact_id,
319 }),
320 },
321 KclValue::Solid { value } => Self::Solid {
322 value: Box::new(OpSolid {
323 artifact_id: value.artifact_id,
324 }),
325 },
326 KclValue::Helix { value } => Self::Helix {
327 value: Box::new(OpHelix {
328 artifact_id: value.artifact_id,
329 }),
330 },
331 KclValue::ImportedGeometry(imported_geometry) => Self::ImportedGeometry {
332 artifact_id: ArtifactId::new(imported_geometry.id),
333 },
334 KclValue::Function { .. } => Self::Function {},
335 KclValue::Module { .. } => Self::Module {},
336 KclValue::KclNone { .. } => Self::KclNone {},
337 KclValue::Type { .. } => Self::Type {},
338 KclValue::BoundedEdge { .. } => Self::BoundedEdge {},
339 }
340 }
341}