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 pub(crate) fn fill_node_paths(&mut self, programs: &crate::execution::ProgramLookup, cached_body_items: usize) {
85 match self {
86 Operation::StdLibCall {
87 node_path,
88 source_range,
89 stdlib_entry_source_range,
90 ..
91 } => {
92 let range = stdlib_entry_source_range.as_ref().unwrap_or(source_range);
97 node_path.fill_placeholder(programs, cached_body_items, *range);
98 }
99 Operation::VariableDeclaration {
100 node_path,
101 source_range,
102 ..
103 }
104 | Operation::GroupBegin {
105 node_path,
106 source_range,
107 ..
108 } => {
109 node_path.fill_placeholder(programs, cached_body_items, *source_range);
110 }
111 Operation::GroupEnd => {}
112 Operation::SketchSolve {
113 node_path,
114 source_range,
115 ..
116 } => {
117 node_path.fill_placeholder(programs, cached_body_items, *source_range);
118 }
119 }
120 }
121}
122
123#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
124#[ts(export_to = "Operation.ts")]
125#[serde(tag = "type")]
126#[expect(clippy::large_enum_variant)]
127pub enum Group {
128 #[serde(rename_all = "camelCase")]
130 FunctionCall {
131 name: Option<String>,
134 function_source_range: SourceRange,
137 unlabeled_arg: Option<OpArg>,
139 labeled_args: IndexMap<String, OpArg>,
141 },
142 #[allow(dead_code)]
144 #[serde(rename_all = "camelCase")]
145 ModuleInstance {
146 name: String,
148 module_id: ModuleId,
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}