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