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