Skip to main content

kcl_lib/execution/
artifact.rs

1use fnv::FnvHashMap;
2use fnv::FnvHashSet;
3use indexmap::IndexMap;
4use kittycad_modeling_cmds::EnableSketchMode;
5use kittycad_modeling_cmds::FaceIsPlanar;
6use kittycad_modeling_cmds::ModelingCmd;
7use kittycad_modeling_cmds::ok_response::OkModelingCmdResponse;
8use kittycad_modeling_cmds::shared::ExtrusionFaceCapType;
9use kittycad_modeling_cmds::websocket::BatchResponse;
10use kittycad_modeling_cmds::websocket::OkWebSocketResponseData;
11use kittycad_modeling_cmds::websocket::WebSocketResponse;
12use kittycad_modeling_cmds::{self as kcmc};
13use serde::Serialize;
14use serde::ser::SerializeSeq;
15use uuid::Uuid;
16
17use crate::KclError;
18use crate::ModuleId;
19use crate::NodePath;
20use crate::SourceRange;
21use crate::engine::PlaneName;
22use crate::errors::KclErrorDetails;
23use crate::execution::ArtifactId;
24use crate::execution::state::ModuleInfoMap;
25use crate::front::Constraint;
26use crate::front::ObjectId;
27use crate::modules::ModulePath;
28use crate::parsing::ast::types::BodyItem;
29use crate::parsing::ast::types::ImportPath;
30use crate::parsing::ast::types::ImportSelector;
31use crate::parsing::ast::types::Node;
32use crate::parsing::ast::types::Program;
33use crate::std::sketch::build_reverse_region_mapping;
34
35#[cfg(test)]
36mod mermaid_tests;
37#[cfg(test)]
38mod tests;
39
40macro_rules! internal_error {
41    ($range:expr, $($rest:tt)*) => {{
42        let message = format!($($rest)*);
43        debug_assert!(false, "{}", &message);
44        return Err(KclError::new_internal(KclErrorDetails::new(message, vec![$range])));
45    }};
46}
47
48/// A command that may create or update artifacts on the TS side.  Because
49/// engine commands are batched, we don't have the response yet when these are
50/// created.
51#[derive(Debug, Clone, PartialEq, Serialize, ts_rs::TS)]
52#[ts(export_to = "Artifact.ts")]
53#[serde(rename_all = "camelCase")]
54pub struct ArtifactCommand {
55    /// Identifier of the command that can be matched with its response.
56    pub cmd_id: Uuid,
57    /// The source range that's the boundary of calling the standard
58    /// library, not necessarily the true source range of the command.
59    pub range: SourceRange,
60    /// The engine command.  Each artifact command is backed by an engine
61    /// command.  In the future, we may need to send information to the TS side
62    /// without an engine command, in which case, we would make this field
63    /// optional.
64    pub command: ModelingCmd,
65}
66
67pub type DummyPathToNode = Vec<()>;
68
69fn serialize_dummy_path_to_node<S>(_path_to_node: &DummyPathToNode, serializer: S) -> Result<S::Ok, S::Error>
70where
71    S: serde::Serializer,
72{
73    // Always output an empty array, for now.
74    let seq = serializer.serialize_seq(Some(0))?;
75    seq.end()
76}
77
78#[derive(Debug, Clone, Default, Serialize, PartialEq, Eq, ts_rs::TS)]
79#[ts(export_to = "Artifact.ts")]
80#[serde(rename_all = "camelCase")]
81pub struct CodeRef {
82    pub range: SourceRange,
83    pub node_path: NodePath,
84    // TODO: We should implement this in Rust.
85    #[serde(default, serialize_with = "serialize_dummy_path_to_node")]
86    #[ts(type = "Array<[string | number, string]>")]
87    pub path_to_node: DummyPathToNode,
88}
89
90impl CodeRef {
91    pub fn placeholder(range: SourceRange) -> Self {
92        Self {
93            range,
94            node_path: Default::default(),
95            path_to_node: Vec::new(),
96        }
97    }
98}
99
100#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
101#[ts(export_to = "Artifact.ts")]
102#[serde(rename_all = "camelCase")]
103pub struct CompositeSolid {
104    pub id: ArtifactId,
105    /// Whether this artifact has been used in a subsequent operation
106    pub consumed: bool,
107    pub sub_type: CompositeSolidSubType,
108    /// Index of this output in the expression result, for operations that
109    /// return multiple selectable bodies from one KCL variable.
110    #[serde(default, skip_serializing_if = "Option::is_none")]
111    pub output_index: Option<usize>,
112    /// Constituent solids of the composite solid.
113    pub solid_ids: Vec<ArtifactId>,
114    /// Tool solids used for asymmetric operations like subtract.
115    pub tool_ids: Vec<ArtifactId>,
116    pub code_ref: CodeRef,
117    /// This is the ID of the composite solid that this is part of, if any, as a
118    /// composite solid can be used as input for another composite solid.
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub composite_solid_id: Option<ArtifactId>,
121    /// Pattern operations that use this composite solid as their source.
122    #[serde(default, skip_serializing_if = "Vec::is_empty")]
123    pub pattern_ids: Vec<ArtifactId>,
124}
125
126#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
127#[ts(export_to = "Artifact.ts")]
128#[serde(rename_all = "camelCase")]
129pub enum CompositeSolidSubType {
130    Intersect,
131    Subtract,
132    Split,
133    Union,
134}
135
136#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
137#[ts(export_to = "Artifact.ts")]
138#[serde(rename_all = "camelCase")]
139pub struct Plane {
140    pub id: ArtifactId,
141    pub path_ids: Vec<ArtifactId>,
142    pub code_ref: CodeRef,
143}
144
145#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
146#[ts(export_to = "Artifact.ts")]
147#[serde(rename_all = "camelCase")]
148pub struct Path {
149    pub id: ArtifactId,
150    pub sub_type: PathSubType,
151    pub plane_id: ArtifactId,
152    pub seg_ids: Vec<ArtifactId>,
153    /// Whether this artifact has been used in a subsequent operation
154    pub consumed: bool,
155    #[serde(default, skip_serializing_if = "Option::is_none")]
156    /// The sweep, if any, that this Path serves as the base path for.
157    /// corresponds to `path_id` on the Sweep.
158    pub sweep_id: Option<ArtifactId>,
159    /// The sweep, if any, that this Path serves as the trajectory for.
160    pub trajectory_sweep_id: Option<ArtifactId>,
161    #[serde(default, skip_serializing_if = "Option::is_none")]
162    pub solid2d_id: Option<ArtifactId>,
163    pub code_ref: CodeRef,
164    /// This is the ID of the composite solid that this is part of, if any, as
165    /// this can be used as input for another composite solid.
166    #[serde(default, skip_serializing_if = "Option::is_none")]
167    pub composite_solid_id: Option<ArtifactId>,
168    /// For sketch paths, the ID of the sketch block this path was created
169    /// from. `None` for region paths and paths created in other ways.
170    #[serde(default, skip_serializing_if = "Option::is_none")]
171    pub sketch_block_id: Option<ArtifactId>,
172    /// For region paths, the ID of the sketch path this region was created
173    /// from. `None` for sketch paths.
174    #[serde(default, skip_serializing_if = "Option::is_none")]
175    pub origin_path_id: Option<ArtifactId>,
176    /// The hole, if any, from a subtract2d() call.
177    #[serde(default, skip_serializing_if = "Option::is_none")]
178    pub inner_path_id: Option<ArtifactId>,
179    /// The `Path` that this is a hole of, if any. The inverse link of
180    /// `inner_path_id`.
181    #[serde(default, skip_serializing_if = "Option::is_none")]
182    pub outer_path_id: Option<ArtifactId>,
183    /// Pattern operations that use this path as their source.
184    #[serde(default, skip_serializing_if = "Vec::is_empty")]
185    pub pattern_ids: Vec<ArtifactId>,
186}
187
188#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
189#[ts(export_to = "Artifact.ts")]
190#[serde(rename_all = "camelCase")]
191pub enum PathSubType {
192    Sketch,
193    Region,
194}
195
196#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
197#[ts(export_to = "Artifact.ts")]
198#[serde(rename_all = "camelCase")]
199pub struct Segment {
200    pub id: ArtifactId,
201    pub path_id: ArtifactId,
202    /// If this artifact is a segment in a region, the segment in the original
203    /// sketch that this was derived from.
204    #[serde(default, skip_serializing_if = "Option::is_none")]
205    pub original_seg_id: Option<ArtifactId>,
206    #[serde(default, skip_serializing_if = "Option::is_none")]
207    pub surface_id: Option<ArtifactId>,
208    pub edge_ids: Vec<ArtifactId>,
209    #[serde(default, skip_serializing_if = "Option::is_none")]
210    pub edge_cut_id: Option<ArtifactId>,
211    pub code_ref: CodeRef,
212    pub common_surface_ids: Vec<ArtifactId>,
213}
214
215/// A sweep is a more generic term for extrude, revolve, loft, sweep, and blend.
216#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
217#[ts(export_to = "Artifact.ts")]
218#[serde(rename_all = "camelCase")]
219pub struct Sweep {
220    pub id: ArtifactId,
221    pub sub_type: SweepSubType,
222    pub path_id: ArtifactId,
223    pub surface_ids: Vec<ArtifactId>,
224    pub edge_ids: Vec<ArtifactId>,
225    pub code_ref: CodeRef,
226    /// ID of trajectory path for sweep, if any
227    /// Only applicable to SweepSubType::Sweep and SweepSubType::Blend, which
228    /// can use a second path-like input
229    pub trajectory_id: Option<ArtifactId>,
230    pub method: kittycad_modeling_cmds::shared::ExtrudeMethod,
231    /// Whether this artifact has been used in a subsequent operation
232    pub consumed: bool,
233    /// Pattern operations that use this sweep as their source.
234    #[serde(default, skip_serializing_if = "Vec::is_empty")]
235    pub pattern_ids: Vec<ArtifactId>,
236}
237
238#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
239#[ts(export_to = "Artifact.ts")]
240#[serde(rename_all = "camelCase")]
241pub enum SweepSubType {
242    Extrusion,
243    ExtrusionTwist,
244    Revolve,
245    RevolveAboutEdge,
246    Loft,
247    Blend,
248    Sweep,
249}
250
251#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
252#[ts(export_to = "Artifact.ts")]
253#[serde(rename_all = "camelCase")]
254pub struct Solid2d {
255    pub id: ArtifactId,
256    pub path_id: ArtifactId,
257}
258
259#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
260#[ts(export_to = "Artifact.ts")]
261#[serde(rename_all = "camelCase")]
262pub struct PrimitiveFace {
263    pub id: ArtifactId,
264    pub solid_id: ArtifactId,
265    pub code_ref: CodeRef,
266}
267
268#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
269#[ts(export_to = "Artifact.ts")]
270#[serde(rename_all = "camelCase")]
271pub struct PrimitiveEdge {
272    pub id: ArtifactId,
273    pub solid_id: ArtifactId,
274    pub code_ref: CodeRef,
275}
276
277#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
278#[ts(export_to = "Artifact.ts")]
279#[serde(rename_all = "camelCase")]
280pub struct PlaneOfFace {
281    pub id: ArtifactId,
282    pub face_id: ArtifactId,
283    pub code_ref: CodeRef,
284}
285
286#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
287#[ts(export_to = "Artifact.ts")]
288#[serde(rename_all = "camelCase")]
289pub struct StartSketchOnFace {
290    pub id: ArtifactId,
291    pub face_id: ArtifactId,
292    pub code_ref: CodeRef,
293}
294
295#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
296#[ts(export_to = "Artifact.ts")]
297#[serde(rename_all = "camelCase")]
298pub struct StartSketchOnPlane {
299    pub id: ArtifactId,
300    pub plane_id: ArtifactId,
301    pub code_ref: CodeRef,
302}
303
304#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
305#[ts(export_to = "Artifact.ts")]
306#[serde(rename_all = "camelCase")]
307pub struct SketchBlock {
308    pub id: ArtifactId,
309    /// The semantic standard plane name when the sketch block is on a standard plane.
310    #[serde(default, skip_serializing_if = "Option::is_none")]
311    pub standard_plane: Option<PlaneName>,
312    /// The concrete plane artifact ID backing the sketch block, when one is available.
313    #[serde(default, skip_serializing_if = "Option::is_none")]
314    pub plane_id: Option<ArtifactId>,
315    /// The path artifact ID created from the sketch block, if there is one.
316    /// There are edge cases when a path isn't created, like when there are no
317    /// segments.
318    #[serde(default, skip_serializing_if = "Option::is_none")]
319    pub path_id: Option<ArtifactId>,
320    pub code_ref: CodeRef,
321    /// The sketch ID (ObjectId) for the sketch scene object.
322    pub sketch_id: ObjectId,
323}
324
325#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
326#[ts(export_to = "Artifact.ts")]
327#[serde(rename_all = "camelCase")]
328pub enum SketchBlockConstraintType {
329    Angle,
330    Coincident,
331    Distance,
332    Diameter,
333    EqualRadius,
334    Fixed,
335    HorizontalDistance,
336    VerticalDistance,
337    Horizontal,
338    LinesEqualLength,
339    Midpoint,
340    Parallel,
341    Perpendicular,
342    Radius,
343    Symmetric,
344    Tangent,
345    Vertical,
346}
347
348impl From<&Constraint> for SketchBlockConstraintType {
349    fn from(constraint: &Constraint) -> Self {
350        match constraint {
351            Constraint::Coincident { .. } => SketchBlockConstraintType::Coincident,
352            Constraint::Distance { .. } => SketchBlockConstraintType::Distance,
353            Constraint::Diameter { .. } => SketchBlockConstraintType::Diameter,
354            Constraint::EqualRadius { .. } => SketchBlockConstraintType::EqualRadius,
355            Constraint::Fixed { .. } => SketchBlockConstraintType::Fixed,
356            Constraint::HorizontalDistance { .. } => SketchBlockConstraintType::HorizontalDistance,
357            Constraint::VerticalDistance { .. } => SketchBlockConstraintType::VerticalDistance,
358            Constraint::Horizontal { .. } => SketchBlockConstraintType::Horizontal,
359            Constraint::LinesEqualLength { .. } => SketchBlockConstraintType::LinesEqualLength,
360            Constraint::Midpoint(..) => SketchBlockConstraintType::Midpoint,
361            Constraint::Parallel { .. } => SketchBlockConstraintType::Parallel,
362            Constraint::Perpendicular { .. } => SketchBlockConstraintType::Perpendicular,
363            Constraint::Radius { .. } => SketchBlockConstraintType::Radius,
364            Constraint::Symmetric { .. } => SketchBlockConstraintType::Symmetric,
365            Constraint::Tangent { .. } => SketchBlockConstraintType::Tangent,
366            Constraint::Vertical { .. } => SketchBlockConstraintType::Vertical,
367            Constraint::Angle(..) => SketchBlockConstraintType::Angle,
368        }
369    }
370}
371
372#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
373#[ts(export_to = "Artifact.ts")]
374#[serde(rename_all = "camelCase")]
375pub struct SketchBlockConstraint {
376    pub id: ArtifactId,
377    /// The sketch ID (ObjectId) that owns this constraint.
378    pub sketch_id: ObjectId,
379    /// The constraint ID (ObjectId) for the constraint scene object.
380    pub constraint_id: ObjectId,
381    pub constraint_type: SketchBlockConstraintType,
382    pub code_ref: CodeRef,
383}
384
385#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
386#[ts(export_to = "Artifact.ts")]
387#[serde(rename_all = "camelCase")]
388pub struct Wall {
389    pub id: ArtifactId,
390    pub seg_id: ArtifactId,
391    pub edge_cut_edge_ids: Vec<ArtifactId>,
392    pub sweep_id: ArtifactId,
393    pub path_ids: Vec<ArtifactId>,
394    /// This is for the sketch-on-face plane, not for the wall itself.  Traverse
395    /// to the extrude and/or segment to get the wall's code_ref.
396    pub face_code_ref: CodeRef,
397    /// The command ID that got the data for this wall. Used for stable sorting.
398    pub cmd_id: uuid::Uuid,
399}
400
401#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
402#[ts(export_to = "Artifact.ts")]
403#[serde(rename_all = "camelCase")]
404pub struct Cap {
405    pub id: ArtifactId,
406    pub sub_type: CapSubType,
407    pub edge_cut_edge_ids: Vec<ArtifactId>,
408    pub sweep_id: ArtifactId,
409    pub path_ids: Vec<ArtifactId>,
410    /// This is for the sketch-on-face plane, not for the cap itself.  Traverse
411    /// to the extrude and/or segment to get the cap's code_ref.
412    pub face_code_ref: CodeRef,
413    /// The command ID that got the data for this cap. Used for stable sorting.
414    pub cmd_id: uuid::Uuid,
415}
416
417#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
418#[ts(export_to = "Artifact.ts")]
419#[serde(rename_all = "camelCase")]
420pub enum CapSubType {
421    Start,
422    End,
423}
424
425#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
426#[ts(export_to = "Artifact.ts")]
427#[serde(rename_all = "camelCase")]
428pub struct SweepEdge {
429    pub id: ArtifactId,
430    pub sub_type: SweepEdgeSubType,
431    pub seg_id: ArtifactId,
432    pub cmd_id: uuid::Uuid,
433    // This is only used for sorting, not for the actual artifact.
434    #[serde(skip)]
435    pub index: usize,
436    pub sweep_id: ArtifactId,
437    pub common_surface_ids: Vec<ArtifactId>,
438}
439
440#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
441#[ts(export_to = "Artifact.ts")]
442#[serde(rename_all = "camelCase")]
443pub enum SweepEdgeSubType {
444    Opposite,
445    Adjacent,
446}
447
448#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
449#[ts(export_to = "Artifact.ts")]
450#[serde(rename_all = "camelCase")]
451pub struct EdgeCut {
452    pub id: ArtifactId,
453    pub sub_type: EdgeCutSubType,
454    pub consumed_edge_id: ArtifactId,
455    pub edge_ids: Vec<ArtifactId>,
456    #[serde(default, skip_serializing_if = "Option::is_none")]
457    pub surface_id: Option<ArtifactId>,
458    pub code_ref: CodeRef,
459}
460
461#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
462#[ts(export_to = "Artifact.ts")]
463#[serde(rename_all = "camelCase")]
464pub enum EdgeCutSubType {
465    Fillet,
466    Chamfer,
467    Custom,
468}
469
470impl From<kcmc::shared::CutType> for EdgeCutSubType {
471    fn from(cut_type: kcmc::shared::CutType) -> Self {
472        match cut_type {
473            kcmc::shared::CutType::Fillet => EdgeCutSubType::Fillet,
474            kcmc::shared::CutType::Chamfer => EdgeCutSubType::Chamfer,
475        }
476    }
477}
478
479impl From<kcmc::shared::CutTypeV2> for EdgeCutSubType {
480    fn from(cut_type: kcmc::shared::CutTypeV2) -> Self {
481        match cut_type {
482            kcmc::shared::CutTypeV2::Fillet { .. } => EdgeCutSubType::Fillet,
483            kcmc::shared::CutTypeV2::Chamfer { .. } => EdgeCutSubType::Chamfer,
484            kcmc::shared::CutTypeV2::Custom { .. } => EdgeCutSubType::Custom,
485            // Modeling API has added something we're not aware of.
486            _other => EdgeCutSubType::Custom,
487        }
488    }
489}
490
491#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
492#[ts(export_to = "Artifact.ts")]
493#[serde(rename_all = "camelCase")]
494pub struct EdgeCutEdge {
495    pub id: ArtifactId,
496    pub edge_cut_id: ArtifactId,
497    pub surface_id: ArtifactId,
498}
499
500#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
501#[ts(export_to = "Artifact.ts")]
502#[serde(rename_all = "camelCase")]
503pub struct Helix {
504    pub id: ArtifactId,
505    /// The axis of the helix.  Currently this is always an edge ID, but we may
506    /// add axes to the graph.
507    pub axis_id: Option<ArtifactId>,
508    pub code_ref: CodeRef,
509    /// The sweep, if any, that this Helix serves as the trajectory for.
510    pub trajectory_sweep_id: Option<ArtifactId>,
511    /// Whether this artifact has been used in a subsequent operation
512    pub consumed: bool,
513}
514
515#[derive(Debug, Clone, Serialize, PartialEq, Eq, ts_rs::TS)]
516#[ts(export_to = "Artifact.ts")]
517#[serde(rename_all = "camelCase")]
518pub struct GdtAnnotationArtifact {
519    pub id: ArtifactId,
520    pub code_ref: CodeRef,
521}
522
523#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
524#[ts(export_to = "Artifact.ts")]
525#[serde(rename_all = "camelCase")]
526pub struct Pattern {
527    pub id: ArtifactId,
528    pub sub_type: PatternSubType,
529    /// Geometry artifact that was the source of the pattern operation.
530    pub source_id: ArtifactId,
531    /// IDs of copied top-level objects created by the pattern operation.
532    pub copy_ids: Vec<ArtifactId>,
533    /// IDs of copied faces created by the pattern operation.
534    pub copy_face_ids: Vec<ArtifactId>,
535    /// IDs of copied edges created by the pattern operation.
536    pub copy_edge_ids: Vec<ArtifactId>,
537    pub code_ref: CodeRef,
538}
539
540#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, ts_rs::TS)]
541#[ts(export_to = "Artifact.ts")]
542#[serde(rename_all = "camelCase")]
543pub enum PatternSubType {
544    Circular,
545    Linear,
546    Transform,
547}
548
549#[derive(Debug, Clone, Serialize, PartialEq, ts_rs::TS)]
550#[ts(export_to = "Artifact.ts")]
551#[serde(tag = "type", rename_all = "camelCase")]
552#[expect(clippy::large_enum_variant)]
553pub enum Artifact {
554    CompositeSolid(CompositeSolid),
555    Plane(Plane),
556    Path(Path),
557    Segment(Segment),
558    Solid2d(Solid2d),
559    PrimitiveFace(PrimitiveFace),
560    PrimitiveEdge(PrimitiveEdge),
561    PlaneOfFace(PlaneOfFace),
562    StartSketchOnFace(StartSketchOnFace),
563    StartSketchOnPlane(StartSketchOnPlane),
564    SketchBlock(SketchBlock),
565    SketchBlockConstraint(SketchBlockConstraint),
566    Sweep(Sweep),
567    Wall(Wall),
568    Cap(Cap),
569    SweepEdge(SweepEdge),
570    EdgeCut(EdgeCut),
571    EdgeCutEdge(EdgeCutEdge),
572    Helix(Helix),
573    GdtAnnotation(GdtAnnotationArtifact),
574    Pattern(Pattern),
575}
576
577impl Artifact {
578    pub(crate) fn id(&self) -> ArtifactId {
579        match self {
580            Artifact::CompositeSolid(a) => a.id,
581            Artifact::Plane(a) => a.id,
582            Artifact::Path(a) => a.id,
583            Artifact::Segment(a) => a.id,
584            Artifact::Solid2d(a) => a.id,
585            Artifact::PrimitiveFace(a) => a.id,
586            Artifact::PrimitiveEdge(a) => a.id,
587            Artifact::StartSketchOnFace(a) => a.id,
588            Artifact::StartSketchOnPlane(a) => a.id,
589            Artifact::SketchBlock(a) => a.id,
590            Artifact::SketchBlockConstraint(a) => a.id,
591            Artifact::PlaneOfFace(a) => a.id,
592            Artifact::Sweep(a) => a.id,
593            Artifact::Wall(a) => a.id,
594            Artifact::Cap(a) => a.id,
595            Artifact::SweepEdge(a) => a.id,
596            Artifact::EdgeCut(a) => a.id,
597            Artifact::EdgeCutEdge(a) => a.id,
598            Artifact::Helix(a) => a.id,
599            Artifact::GdtAnnotation(a) => a.id,
600            Artifact::Pattern(a) => a.id,
601        }
602    }
603
604    /// The [`CodeRef`] for the artifact itself. See also
605    /// [`Self::face_code_ref`].
606    pub fn code_ref(&self) -> Option<&CodeRef> {
607        match self {
608            Artifact::CompositeSolid(a) => Some(&a.code_ref),
609            Artifact::Plane(a) => Some(&a.code_ref),
610            Artifact::Path(a) => Some(&a.code_ref),
611            Artifact::Segment(a) => Some(&a.code_ref),
612            Artifact::Solid2d(_) => None,
613            Artifact::PrimitiveFace(a) => Some(&a.code_ref),
614            Artifact::PrimitiveEdge(a) => Some(&a.code_ref),
615            Artifact::StartSketchOnFace(a) => Some(&a.code_ref),
616            Artifact::StartSketchOnPlane(a) => Some(&a.code_ref),
617            Artifact::SketchBlock(a) => Some(&a.code_ref),
618            Artifact::SketchBlockConstraint(a) => Some(&a.code_ref),
619            Artifact::PlaneOfFace(a) => Some(&a.code_ref),
620            Artifact::Sweep(a) => Some(&a.code_ref),
621            Artifact::Wall(_) => None,
622            Artifact::Cap(_) => None,
623            Artifact::SweepEdge(_) => None,
624            Artifact::EdgeCut(a) => Some(&a.code_ref),
625            Artifact::EdgeCutEdge(_) => None,
626            Artifact::Helix(a) => Some(&a.code_ref),
627            Artifact::GdtAnnotation(a) => Some(&a.code_ref),
628            Artifact::Pattern(a) => Some(&a.code_ref),
629        }
630    }
631
632    /// The [`CodeRef`] referring to the face artifact that it's on, not the
633    /// artifact itself.
634    pub fn face_code_ref(&self) -> Option<&CodeRef> {
635        match self {
636            Artifact::CompositeSolid(_)
637            | Artifact::Plane(_)
638            | Artifact::Path(_)
639            | Artifact::Segment(_)
640            | Artifact::Solid2d(_)
641            | Artifact::PrimitiveEdge(_)
642            | Artifact::StartSketchOnFace(_)
643            | Artifact::PlaneOfFace(_)
644            | Artifact::StartSketchOnPlane(_)
645            | Artifact::SketchBlock(_)
646            | Artifact::SketchBlockConstraint(_)
647            | Artifact::Sweep(_) => None,
648            Artifact::PrimitiveFace(a) => Some(&a.code_ref),
649            Artifact::Wall(a) => Some(&a.face_code_ref),
650            Artifact::Cap(a) => Some(&a.face_code_ref),
651            Artifact::SweepEdge(_)
652            | Artifact::EdgeCut(_)
653            | Artifact::EdgeCutEdge(_)
654            | Artifact::Helix(_)
655            | Artifact::GdtAnnotation(_)
656            | Artifact::Pattern(_) => None,
657        }
658    }
659
660    /// Merge the new artifact into self.  If it can't because it's a different
661    /// type, return the new artifact which should be used as a replacement.
662    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
663        match self {
664            Artifact::CompositeSolid(a) => a.merge(new),
665            Artifact::Plane(a) => a.merge(new),
666            Artifact::Path(a) => a.merge(new),
667            Artifact::Segment(a) => a.merge(new),
668            Artifact::Solid2d(_) => Some(new),
669            Artifact::PrimitiveFace(_) => Some(new),
670            Artifact::PrimitiveEdge(_) => Some(new),
671            Artifact::StartSketchOnFace { .. } => Some(new),
672            Artifact::StartSketchOnPlane { .. } => Some(new),
673            Artifact::SketchBlock { .. } => Some(new),
674            Artifact::SketchBlockConstraint { .. } => Some(new),
675            Artifact::PlaneOfFace { .. } => Some(new),
676            Artifact::Sweep(a) => a.merge(new),
677            Artifact::Wall(a) => a.merge(new),
678            Artifact::Cap(a) => a.merge(new),
679            Artifact::SweepEdge(_) => Some(new),
680            Artifact::EdgeCut(a) => a.merge(new),
681            Artifact::EdgeCutEdge(_) => Some(new),
682            Artifact::Helix(a) => a.merge(new),
683            Artifact::GdtAnnotation(a) => a.merge(new),
684            Artifact::Pattern(a) => a.merge(new),
685        }
686    }
687}
688
689impl CompositeSolid {
690    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
691        let Artifact::CompositeSolid(new) = new else {
692            return Some(new);
693        };
694        merge_ids(&mut self.solid_ids, new.solid_ids);
695        merge_ids(&mut self.tool_ids, new.tool_ids);
696        merge_opt_id(&mut self.composite_solid_id, new.composite_solid_id);
697        merge_ids(&mut self.pattern_ids, new.pattern_ids);
698        self.output_index = new.output_index;
699        self.consumed = new.consumed;
700
701        None
702    }
703}
704
705impl Plane {
706    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
707        let Artifact::Plane(new) = new else {
708            return Some(new);
709        };
710        merge_ids(&mut self.path_ids, new.path_ids);
711
712        None
713    }
714}
715
716impl Path {
717    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
718        let Artifact::Path(new) = new else {
719            return Some(new);
720        };
721        merge_opt_id(&mut self.sweep_id, new.sweep_id);
722        merge_opt_id(&mut self.trajectory_sweep_id, new.trajectory_sweep_id);
723        merge_ids(&mut self.seg_ids, new.seg_ids);
724        merge_opt_id(&mut self.solid2d_id, new.solid2d_id);
725        merge_opt_id(&mut self.composite_solid_id, new.composite_solid_id);
726        merge_opt_id(&mut self.sketch_block_id, new.sketch_block_id);
727        merge_opt_id(&mut self.origin_path_id, new.origin_path_id);
728        merge_opt_id(&mut self.inner_path_id, new.inner_path_id);
729        merge_opt_id(&mut self.outer_path_id, new.outer_path_id);
730        merge_ids(&mut self.pattern_ids, new.pattern_ids);
731        self.consumed = new.consumed;
732
733        None
734    }
735}
736
737impl Segment {
738    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
739        let Artifact::Segment(new) = new else {
740            return Some(new);
741        };
742        merge_opt_id(&mut self.original_seg_id, new.original_seg_id);
743        merge_opt_id(&mut self.surface_id, new.surface_id);
744        merge_ids(&mut self.edge_ids, new.edge_ids);
745        merge_opt_id(&mut self.edge_cut_id, new.edge_cut_id);
746        merge_ids(&mut self.common_surface_ids, new.common_surface_ids);
747
748        None
749    }
750}
751
752impl Sweep {
753    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
754        let Artifact::Sweep(new) = new else {
755            return Some(new);
756        };
757        merge_ids(&mut self.surface_ids, new.surface_ids);
758        merge_ids(&mut self.edge_ids, new.edge_ids);
759        merge_opt_id(&mut self.trajectory_id, new.trajectory_id);
760        merge_ids(&mut self.pattern_ids, new.pattern_ids);
761        self.consumed = new.consumed;
762
763        None
764    }
765}
766
767impl Wall {
768    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
769        let Artifact::Wall(new) = new else {
770            return Some(new);
771        };
772        merge_ids(&mut self.edge_cut_edge_ids, new.edge_cut_edge_ids);
773        merge_ids(&mut self.path_ids, new.path_ids);
774
775        None
776    }
777}
778
779impl Cap {
780    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
781        let Artifact::Cap(new) = new else {
782            return Some(new);
783        };
784        merge_ids(&mut self.edge_cut_edge_ids, new.edge_cut_edge_ids);
785        merge_ids(&mut self.path_ids, new.path_ids);
786
787        None
788    }
789}
790
791impl EdgeCut {
792    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
793        let Artifact::EdgeCut(new) = new else {
794            return Some(new);
795        };
796        merge_opt_id(&mut self.surface_id, new.surface_id);
797        merge_ids(&mut self.edge_ids, new.edge_ids);
798
799        None
800    }
801}
802
803impl Helix {
804    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
805        let Artifact::Helix(new) = new else {
806            return Some(new);
807        };
808        merge_opt_id(&mut self.axis_id, new.axis_id);
809        merge_opt_id(&mut self.trajectory_sweep_id, new.trajectory_sweep_id);
810        self.consumed = new.consumed;
811
812        None
813    }
814}
815
816impl GdtAnnotationArtifact {
817    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
818        let Artifact::GdtAnnotation(new) = new else {
819            return Some(new);
820        };
821        self.code_ref = new.code_ref;
822
823        None
824    }
825}
826
827impl Pattern {
828    fn merge(&mut self, new: Artifact) -> Option<Artifact> {
829        let Artifact::Pattern(new) = new else {
830            return Some(new);
831        };
832        merge_ids(&mut self.copy_ids, new.copy_ids);
833        merge_ids(&mut self.copy_face_ids, new.copy_face_ids);
834        merge_ids(&mut self.copy_edge_ids, new.copy_edge_ids);
835
836        None
837    }
838}
839
840#[derive(Debug, Clone, Default, PartialEq, Serialize, ts_rs::TS)]
841#[ts(export_to = "Artifact.ts")]
842#[serde(rename_all = "camelCase")]
843pub struct ArtifactGraph {
844    map: IndexMap<ArtifactId, Artifact>,
845    pub(super) item_count: usize,
846}
847
848impl ArtifactGraph {
849    pub fn get(&self, id: &ArtifactId) -> Option<&Artifact> {
850        self.map.get(id)
851    }
852
853    pub fn len(&self) -> usize {
854        self.map.len()
855    }
856
857    pub fn is_empty(&self) -> bool {
858        self.map.is_empty()
859    }
860
861    #[cfg(test)]
862    pub(crate) fn iter(&self) -> impl Iterator<Item = (&ArtifactId, &Artifact)> {
863        self.map.iter()
864    }
865
866    pub fn values(&self) -> impl Iterator<Item = &Artifact> {
867        self.map.values()
868    }
869
870    pub fn clear(&mut self) {
871        self.map.clear();
872        self.item_count = 0;
873    }
874
875    /// Consume the artifact graph and return the map of artifacts.
876    fn into_map(self) -> IndexMap<ArtifactId, Artifact> {
877        self.map
878    }
879}
880
881#[derive(Debug, Clone)]
882struct ImportCodeRef {
883    node_path: NodePath,
884    range: SourceRange,
885}
886
887fn import_statement_code_refs(
888    ast: &Node<Program>,
889    module_infos: &ModuleInfoMap,
890    programs: &crate::execution::ProgramLookup,
891    cached_body_items: usize,
892) -> FnvHashMap<ModuleId, ImportCodeRef> {
893    let mut code_refs = FnvHashMap::default();
894    for body_item in &ast.body {
895        let BodyItem::ImportStatement(import_stmt) = body_item else {
896            continue;
897        };
898        if !matches!(import_stmt.selector, ImportSelector::None { .. }) {
899            continue;
900        }
901        let Some(module_id) = module_id_for_import_path(module_infos, &import_stmt.path) else {
902            continue;
903        };
904        let range = SourceRange::from(import_stmt);
905        let node_path = NodePath::from_range(programs, cached_body_items, range).unwrap_or_default();
906        code_refs.entry(module_id).or_insert(ImportCodeRef { node_path, range });
907    }
908    code_refs
909}
910
911fn module_id_for_import_path(module_infos: &ModuleInfoMap, import_path: &ImportPath) -> Option<ModuleId> {
912    let import_path = match import_path {
913        ImportPath::Kcl { filename } => filename,
914        ImportPath::Foreign { path } => path,
915        ImportPath::Std { .. } => return None,
916    };
917
918    module_infos.iter().find_map(|(module_id, module_info)| {
919        if let ModulePath::Local {
920            original_import_path: Some(original_import_path),
921            ..
922        } = &module_info.path
923            && original_import_path == import_path
924        {
925            return Some(*module_id);
926        }
927        None
928    })
929}
930
931fn code_ref_for_range(
932    programs: &crate::execution::ProgramLookup,
933    cached_body_items: usize,
934    range: SourceRange,
935    import_code_refs: &FnvHashMap<ModuleId, ImportCodeRef>,
936) -> (SourceRange, NodePath) {
937    if let Some(code_ref) = import_code_refs.get(&range.module_id()) {
938        return (code_ref.range, code_ref.node_path.clone());
939    }
940
941    (
942        range,
943        NodePath::from_range(programs, cached_body_items, range).unwrap_or_default(),
944    )
945}
946
947/// Build the artifact graph from the artifact commands and the responses.  The
948/// initial graph is the graph cached from a previous execution.  NodePaths of
949/// `exec_artifacts` are filled in from the AST.
950pub(super) fn build_artifact_graph(
951    artifact_commands: &[ArtifactCommand],
952    responses: &IndexMap<Uuid, WebSocketResponse>,
953    ast: &Node<Program>,
954    exec_artifacts: &mut IndexMap<ArtifactId, Artifact>,
955    initial_graph: ArtifactGraph,
956    programs: &crate::execution::ProgramLookup,
957    module_infos: &ModuleInfoMap,
958) -> Result<ArtifactGraph, KclError> {
959    let item_count = initial_graph.item_count;
960    let mut map = initial_graph.into_map();
961
962    let mut path_to_plane_id_map = FnvHashMap::default();
963    let mut current_plane_id = None;
964    let import_code_refs = import_statement_code_refs(ast, module_infos, programs, item_count);
965    let flattened_responses = flatten_modeling_command_responses(responses);
966    let entity_clone_id_maps = build_entity_clone_id_maps(artifact_commands, &flattened_responses);
967
968    // Fill in NodePaths for artifacts that were added directly to the map
969    // during execution.
970    for exec_artifact in exec_artifacts.values_mut() {
971        // Note: We only have access to the new AST. So if these artifacts
972        // somehow came from cached AST, this won't fill in anything.
973        fill_in_node_paths(exec_artifact, programs, item_count, &import_code_refs);
974    }
975
976    for artifact_command in artifact_commands {
977        if let ModelingCmd::EnableSketchMode(EnableSketchMode { entity_id, .. }) = artifact_command.command {
978            current_plane_id = Some(entity_id);
979        }
980        // If we get a start path command, we need to set the plane ID to the
981        // current plane ID.
982        // THIS IS THE ONLY THING WE CAN ASSUME IS ALWAYS SEQUENTIAL SINCE ITS PART OF THE
983        // SAME ATOMIC COMMANDS BATCHING.
984        if let ModelingCmd::StartPath(_) = artifact_command.command
985            && let Some(plane_id) = current_plane_id
986        {
987            path_to_plane_id_map.insert(artifact_command.cmd_id, plane_id);
988        }
989        if let ModelingCmd::SketchModeDisable(_) = artifact_command.command {
990            current_plane_id = None;
991        }
992
993        let artifact_updates = artifacts_to_update(
994            &map,
995            artifact_command,
996            &flattened_responses,
997            &entity_clone_id_maps,
998            &path_to_plane_id_map,
999            programs,
1000            item_count,
1001            exec_artifacts,
1002            &import_code_refs,
1003        )?;
1004        for artifact in artifact_updates {
1005            // Merge with existing artifacts.
1006            merge_artifact_into_map(&mut map, artifact);
1007        }
1008    }
1009
1010    for exec_artifact in exec_artifacts.values() {
1011        merge_artifact_into_map(&mut map, exec_artifact.clone());
1012    }
1013
1014    Ok(ArtifactGraph {
1015        map,
1016        item_count: item_count + ast.body.len(),
1017    })
1018}
1019
1020/// These may have been created with placeholder `CodeRef`s because we didn't
1021/// have the entire AST available. Now we fill them in.
1022fn fill_in_node_paths(
1023    artifact: &mut Artifact,
1024    programs: &crate::execution::ProgramLookup,
1025    cached_body_items: usize,
1026    import_code_refs: &FnvHashMap<ModuleId, ImportCodeRef>,
1027) {
1028    match artifact {
1029        Artifact::StartSketchOnFace(face) if face.code_ref.node_path.is_empty() => {
1030            let (range, node_path) =
1031                code_ref_for_range(programs, cached_body_items, face.code_ref.range, import_code_refs);
1032            face.code_ref.range = range;
1033            face.code_ref.node_path = node_path;
1034        }
1035        Artifact::StartSketchOnPlane(plane) if plane.code_ref.node_path.is_empty() => {
1036            let (range, node_path) =
1037                code_ref_for_range(programs, cached_body_items, plane.code_ref.range, import_code_refs);
1038            plane.code_ref.range = range;
1039            plane.code_ref.node_path = node_path;
1040        }
1041        Artifact::SketchBlock(block) if block.code_ref.node_path.is_empty() => {
1042            let (range, node_path) =
1043                code_ref_for_range(programs, cached_body_items, block.code_ref.range, import_code_refs);
1044            block.code_ref.range = range;
1045            block.code_ref.node_path = node_path;
1046        }
1047        Artifact::SketchBlockConstraint(constraint) if constraint.code_ref.node_path.is_empty() => {
1048            constraint.code_ref.node_path =
1049                NodePath::from_range(programs, cached_body_items, constraint.code_ref.range).unwrap_or_default();
1050        }
1051        Artifact::GdtAnnotation(annotation) if annotation.code_ref.node_path.is_empty() => {
1052            let (range, node_path) =
1053                code_ref_for_range(programs, cached_body_items, annotation.code_ref.range, import_code_refs);
1054            annotation.code_ref.range = range;
1055            annotation.code_ref.node_path = node_path;
1056        }
1057        _ => {}
1058    }
1059}
1060
1061/// Flatten the responses into a map of command IDs to modeling command
1062/// responses.  The raw responses from the engine contain batches.
1063fn flatten_modeling_command_responses(
1064    responses: &IndexMap<Uuid, WebSocketResponse>,
1065) -> FnvHashMap<Uuid, OkModelingCmdResponse> {
1066    let mut map = FnvHashMap::default();
1067    for (cmd_id, ws_response) in responses {
1068        let WebSocketResponse::Success(response) = ws_response else {
1069            // Response not successful.
1070            continue;
1071        };
1072        match &response.resp {
1073            OkWebSocketResponseData::Modeling { modeling_response } => {
1074                map.insert(*cmd_id, modeling_response.clone());
1075            }
1076            OkWebSocketResponseData::ModelingBatch { responses } =>
1077            {
1078                #[expect(
1079                    clippy::iter_over_hash_type,
1080                    reason = "Since we're moving entries to another unordered map, it's fine that the order is undefined"
1081                )]
1082                for (cmd_id, batch_response) in responses {
1083                    if let BatchResponse::Success {
1084                        response: modeling_response,
1085                    } = batch_response
1086                    {
1087                        map.insert(*cmd_id.as_ref(), modeling_response.clone());
1088                    }
1089                }
1090            }
1091            OkWebSocketResponseData::IceServerInfo { .. }
1092            | OkWebSocketResponseData::TrickleIce { .. }
1093            | OkWebSocketResponseData::SdpAnswer { .. }
1094            | OkWebSocketResponseData::Export { .. }
1095            | OkWebSocketResponseData::MetricsRequest { .. }
1096            | OkWebSocketResponseData::ModelingSessionData { .. }
1097            | OkWebSocketResponseData::Debug { .. }
1098            | OkWebSocketResponseData::Pong { .. } => {}
1099            _other => {}
1100        }
1101    }
1102
1103    map
1104}
1105
1106#[derive(Debug, Clone)]
1107struct PendingEntityCloneMapping {
1108    clone_cmd_id: Uuid,
1109    old_entity_id: Uuid,
1110    old_child_ids: Option<Vec<Uuid>>,
1111}
1112
1113/// Build old->new entity ID maps for each clone command by pairing the
1114/// `EntityGetAllChildUuids` queries emitted by `std::clone`.
1115fn build_entity_clone_id_maps(
1116    artifact_commands: &[ArtifactCommand],
1117    responses: &FnvHashMap<Uuid, OkModelingCmdResponse>,
1118) -> FnvHashMap<Uuid, FnvHashMap<ArtifactId, ArtifactId>> {
1119    let mut clone_id_maps = FnvHashMap::default();
1120    let mut pending = Vec::new();
1121
1122    for artifact_command in artifact_commands {
1123        match &artifact_command.command {
1124            ModelingCmd::EntityClone(kcmc::EntityClone { entity_id, .. }) => {
1125                pending.push(PendingEntityCloneMapping {
1126                    clone_cmd_id: artifact_command.cmd_id,
1127                    old_entity_id: *entity_id,
1128                    old_child_ids: None,
1129                });
1130            }
1131            ModelingCmd::EntityGetAllChildUuids(kcmc::EntityGetAllChildUuids { entity_id, .. }) => {
1132                let Some(OkModelingCmdResponse::EntityGetAllChildUuids(child_ids_response)) =
1133                    responses.get(&artifact_command.cmd_id)
1134                else {
1135                    continue;
1136                };
1137                let child_ids = child_ids_response.entity_ids.clone();
1138
1139                let mut completed_index = None;
1140                for index in (0..pending.len()).rev() {
1141                    let pending_map = &mut pending[index];
1142                    if pending_map.old_child_ids.is_none() && *entity_id == pending_map.old_entity_id {
1143                        pending_map.old_child_ids = Some(child_ids.clone());
1144                        break;
1145                    }
1146                    if let Some(old_child_ids) = &pending_map.old_child_ids
1147                        && *entity_id == pending_map.clone_cmd_id
1148                    {
1149                        let mut id_map = FnvHashMap::default();
1150                        id_map.insert(
1151                            ArtifactId::new(pending_map.old_entity_id),
1152                            ArtifactId::new(pending_map.clone_cmd_id),
1153                        );
1154                        for (old_id, new_id) in old_child_ids.iter().zip(child_ids.iter()) {
1155                            id_map.insert(ArtifactId::new(*old_id), ArtifactId::new(*new_id));
1156                        }
1157                        clone_id_maps.insert(pending_map.clone_cmd_id, id_map);
1158                        completed_index = Some(index);
1159                        break;
1160                    }
1161                }
1162
1163                if let Some(index) = completed_index {
1164                    pending.swap_remove(index);
1165                }
1166            }
1167            _ => {}
1168        }
1169    }
1170
1171    clone_id_maps
1172}
1173
1174fn merge_artifact_into_map(map: &mut IndexMap<ArtifactId, Artifact>, new_artifact: Artifact) {
1175    fn is_primitive_artifact(artifact: &Artifact) -> bool {
1176        matches!(artifact, Artifact::PrimitiveFace(_) | Artifact::PrimitiveEdge(_))
1177    }
1178
1179    let id = new_artifact.id();
1180    let Some(old_artifact) = map.get_mut(&id) else {
1181        // No old artifact exists.  Insert the new one.
1182        map.insert(id, new_artifact);
1183        return;
1184    };
1185
1186    // Primitive lookups (faceId/edgeId) may resolve to an ID that already has
1187    // a richer artifact (for example Segment/Cap/Wall). Keep the existing node
1188    // to avoid erasing structural graph links.
1189    if is_primitive_artifact(&new_artifact) && !is_primitive_artifact(old_artifact) {
1190        return;
1191    }
1192
1193    if let Some(replacement) = old_artifact.merge(new_artifact) {
1194        *old_artifact = replacement;
1195    }
1196}
1197
1198/// Merge the new IDs into the base vector, avoiding duplicates.  This is O(nm)
1199/// runtime.  Rationale is that most of the ID collections in the artifact graph
1200/// are pretty small, but we may want to change this in the future.
1201fn merge_ids(base: &mut Vec<ArtifactId>, new: Vec<ArtifactId>) {
1202    let original_len = base.len();
1203    for id in new {
1204        // Don't bother inspecting new items that we just pushed.
1205        let original_base = &base[..original_len];
1206        if !original_base.contains(&id) {
1207            base.push(id);
1208        }
1209    }
1210}
1211
1212/// Merge optional Artifact ID
1213fn merge_opt_id(base: &mut Option<ArtifactId>, new: Option<ArtifactId>) {
1214    // Always use the new one, even if it clears it.
1215    *base = new;
1216}
1217
1218fn remap_id_for_clone(id: ArtifactId, entity_id_map: &FnvHashMap<ArtifactId, ArtifactId>) -> ArtifactId {
1219    entity_id_map.get(&id).copied().unwrap_or(id)
1220}
1221
1222fn remap_opt_id_for_clone(
1223    id: Option<ArtifactId>,
1224    entity_id_map: &FnvHashMap<ArtifactId, ArtifactId>,
1225) -> Option<ArtifactId> {
1226    id.map(|id| remap_id_for_clone(id, entity_id_map))
1227}
1228
1229fn remap_ids_for_clone(ids: &[ArtifactId], entity_id_map: &FnvHashMap<ArtifactId, ArtifactId>) -> Vec<ArtifactId> {
1230    ids.iter()
1231        .copied()
1232        .map(|id| remap_id_for_clone(id, entity_id_map))
1233        .collect()
1234}
1235
1236fn remap_mapped_ids_for_clone(
1237    ids: &[ArtifactId],
1238    entity_id_map: &FnvHashMap<ArtifactId, ArtifactId>,
1239) -> Vec<ArtifactId> {
1240    ids.iter().filter_map(|id| entity_id_map.get(id).copied()).collect()
1241}
1242
1243fn remap_artifact_for_clone(
1244    artifact: &Artifact,
1245    entity_id_map: &FnvHashMap<ArtifactId, ArtifactId>,
1246    clone_code_ref: &CodeRef,
1247    clone_cmd_id: Uuid,
1248    source_root_id: ArtifactId,
1249) -> Artifact {
1250    match artifact {
1251        Artifact::CompositeSolid(source) => Artifact::CompositeSolid(CompositeSolid {
1252            id: remap_id_for_clone(source.id, entity_id_map),
1253            consumed: if source.id == source_root_id {
1254                false
1255            } else {
1256                source.consumed
1257            },
1258            sub_type: source.sub_type,
1259            output_index: source.output_index,
1260            solid_ids: remap_ids_for_clone(&source.solid_ids, entity_id_map),
1261            tool_ids: remap_ids_for_clone(&source.tool_ids, entity_id_map),
1262            pattern_ids: remap_mapped_ids_for_clone(&source.pattern_ids, entity_id_map),
1263            code_ref: clone_code_ref.clone(),
1264            composite_solid_id: remap_opt_id_for_clone(source.composite_solid_id, entity_id_map),
1265        }),
1266        Artifact::Plane(source) => Artifact::Plane(Plane {
1267            id: remap_id_for_clone(source.id, entity_id_map),
1268            path_ids: remap_ids_for_clone(&source.path_ids, entity_id_map),
1269            code_ref: clone_code_ref.clone(),
1270        }),
1271        Artifact::Path(source) => Artifact::Path(Path {
1272            id: remap_id_for_clone(source.id, entity_id_map),
1273            sub_type: source.sub_type,
1274            plane_id: remap_id_for_clone(source.plane_id, entity_id_map),
1275            seg_ids: remap_ids_for_clone(&source.seg_ids, entity_id_map),
1276            consumed: if source.id == source_root_id {
1277                false
1278            } else {
1279                source.consumed
1280            },
1281            sweep_id: remap_opt_id_for_clone(source.sweep_id, entity_id_map),
1282            trajectory_sweep_id: remap_opt_id_for_clone(source.trajectory_sweep_id, entity_id_map),
1283            solid2d_id: remap_opt_id_for_clone(source.solid2d_id, entity_id_map),
1284            code_ref: clone_code_ref.clone(),
1285            composite_solid_id: remap_opt_id_for_clone(source.composite_solid_id, entity_id_map),
1286            sketch_block_id: remap_opt_id_for_clone(source.sketch_block_id, entity_id_map),
1287            origin_path_id: remap_opt_id_for_clone(source.origin_path_id, entity_id_map),
1288            inner_path_id: remap_opt_id_for_clone(source.inner_path_id, entity_id_map),
1289            outer_path_id: remap_opt_id_for_clone(source.outer_path_id, entity_id_map),
1290            pattern_ids: remap_mapped_ids_for_clone(&source.pattern_ids, entity_id_map),
1291        }),
1292        Artifact::Segment(source) => Artifact::Segment(Segment {
1293            id: remap_id_for_clone(source.id, entity_id_map),
1294            path_id: remap_id_for_clone(source.path_id, entity_id_map),
1295            original_seg_id: remap_opt_id_for_clone(source.original_seg_id, entity_id_map),
1296            surface_id: remap_opt_id_for_clone(source.surface_id, entity_id_map),
1297            edge_ids: remap_ids_for_clone(&source.edge_ids, entity_id_map),
1298            edge_cut_id: remap_opt_id_for_clone(source.edge_cut_id, entity_id_map),
1299            code_ref: clone_code_ref.clone(),
1300            common_surface_ids: remap_ids_for_clone(&source.common_surface_ids, entity_id_map),
1301        }),
1302        Artifact::Solid2d(source) => Artifact::Solid2d(Solid2d {
1303            id: remap_id_for_clone(source.id, entity_id_map),
1304            path_id: remap_id_for_clone(source.path_id, entity_id_map),
1305        }),
1306        Artifact::PrimitiveFace(source) => Artifact::PrimitiveFace(PrimitiveFace {
1307            id: remap_id_for_clone(source.id, entity_id_map),
1308            solid_id: remap_id_for_clone(source.solid_id, entity_id_map),
1309            code_ref: clone_code_ref.clone(),
1310        }),
1311        Artifact::PrimitiveEdge(source) => Artifact::PrimitiveEdge(PrimitiveEdge {
1312            id: remap_id_for_clone(source.id, entity_id_map),
1313            solid_id: remap_id_for_clone(source.solid_id, entity_id_map),
1314            code_ref: clone_code_ref.clone(),
1315        }),
1316        Artifact::PlaneOfFace(source) => Artifact::PlaneOfFace(PlaneOfFace {
1317            id: remap_id_for_clone(source.id, entity_id_map),
1318            face_id: remap_id_for_clone(source.face_id, entity_id_map),
1319            code_ref: clone_code_ref.clone(),
1320        }),
1321        Artifact::StartSketchOnFace(source) => Artifact::StartSketchOnFace(StartSketchOnFace {
1322            id: remap_id_for_clone(source.id, entity_id_map),
1323            face_id: remap_id_for_clone(source.face_id, entity_id_map),
1324            code_ref: clone_code_ref.clone(),
1325        }),
1326        Artifact::StartSketchOnPlane(source) => Artifact::StartSketchOnPlane(StartSketchOnPlane {
1327            id: remap_id_for_clone(source.id, entity_id_map),
1328            plane_id: remap_id_for_clone(source.plane_id, entity_id_map),
1329            code_ref: clone_code_ref.clone(),
1330        }),
1331        Artifact::SketchBlock(source) => Artifact::SketchBlock(SketchBlock {
1332            id: remap_id_for_clone(source.id, entity_id_map),
1333            standard_plane: source.standard_plane,
1334            plane_id: remap_opt_id_for_clone(source.plane_id, entity_id_map),
1335            path_id: remap_opt_id_for_clone(source.path_id, entity_id_map),
1336            code_ref: clone_code_ref.clone(),
1337            sketch_id: source.sketch_id,
1338        }),
1339        Artifact::SketchBlockConstraint(source) => Artifact::SketchBlockConstraint(SketchBlockConstraint {
1340            id: remap_id_for_clone(source.id, entity_id_map),
1341            sketch_id: source.sketch_id,
1342            constraint_id: source.constraint_id,
1343            constraint_type: source.constraint_type,
1344            code_ref: clone_code_ref.clone(),
1345        }),
1346        Artifact::Sweep(source) => Artifact::Sweep(Sweep {
1347            id: remap_id_for_clone(source.id, entity_id_map),
1348            sub_type: source.sub_type,
1349            path_id: remap_id_for_clone(source.path_id, entity_id_map),
1350            surface_ids: remap_ids_for_clone(&source.surface_ids, entity_id_map),
1351            edge_ids: remap_ids_for_clone(&source.edge_ids, entity_id_map),
1352            code_ref: clone_code_ref.clone(),
1353            trajectory_id: remap_opt_id_for_clone(source.trajectory_id, entity_id_map),
1354            method: source.method,
1355            consumed: if source.id == source_root_id {
1356                false
1357            } else {
1358                source.consumed
1359            },
1360            pattern_ids: remap_mapped_ids_for_clone(&source.pattern_ids, entity_id_map),
1361        }),
1362        Artifact::Wall(source) => Artifact::Wall(Wall {
1363            id: remap_id_for_clone(source.id, entity_id_map),
1364            seg_id: remap_id_for_clone(source.seg_id, entity_id_map),
1365            edge_cut_edge_ids: remap_ids_for_clone(&source.edge_cut_edge_ids, entity_id_map),
1366            sweep_id: remap_id_for_clone(source.sweep_id, entity_id_map),
1367            path_ids: remap_ids_for_clone(&source.path_ids, entity_id_map),
1368            face_code_ref: source.face_code_ref.clone(),
1369            cmd_id: clone_cmd_id,
1370        }),
1371        Artifact::Cap(source) => Artifact::Cap(Cap {
1372            id: remap_id_for_clone(source.id, entity_id_map),
1373            sub_type: source.sub_type,
1374            edge_cut_edge_ids: remap_ids_for_clone(&source.edge_cut_edge_ids, entity_id_map),
1375            sweep_id: remap_id_for_clone(source.sweep_id, entity_id_map),
1376            path_ids: remap_ids_for_clone(&source.path_ids, entity_id_map),
1377            face_code_ref: source.face_code_ref.clone(),
1378            cmd_id: clone_cmd_id,
1379        }),
1380        Artifact::SweepEdge(source) => Artifact::SweepEdge(SweepEdge {
1381            id: remap_id_for_clone(source.id, entity_id_map),
1382            sub_type: source.sub_type,
1383            seg_id: remap_id_for_clone(source.seg_id, entity_id_map),
1384            cmd_id: clone_cmd_id,
1385            index: source.index,
1386            sweep_id: remap_id_for_clone(source.sweep_id, entity_id_map),
1387            common_surface_ids: remap_ids_for_clone(&source.common_surface_ids, entity_id_map),
1388        }),
1389        Artifact::EdgeCut(source) => Artifact::EdgeCut(EdgeCut {
1390            id: remap_id_for_clone(source.id, entity_id_map),
1391            sub_type: source.sub_type,
1392            consumed_edge_id: remap_id_for_clone(source.consumed_edge_id, entity_id_map),
1393            edge_ids: remap_ids_for_clone(&source.edge_ids, entity_id_map),
1394            surface_id: remap_opt_id_for_clone(source.surface_id, entity_id_map),
1395            code_ref: clone_code_ref.clone(),
1396        }),
1397        Artifact::EdgeCutEdge(source) => Artifact::EdgeCutEdge(EdgeCutEdge {
1398            id: remap_id_for_clone(source.id, entity_id_map),
1399            edge_cut_id: remap_id_for_clone(source.edge_cut_id, entity_id_map),
1400            surface_id: remap_id_for_clone(source.surface_id, entity_id_map),
1401        }),
1402        Artifact::Helix(source) => Artifact::Helix(Helix {
1403            id: remap_id_for_clone(source.id, entity_id_map),
1404            axis_id: remap_opt_id_for_clone(source.axis_id, entity_id_map),
1405            code_ref: clone_code_ref.clone(),
1406            trajectory_sweep_id: remap_opt_id_for_clone(source.trajectory_sweep_id, entity_id_map),
1407            consumed: if source.id == source_root_id {
1408                false
1409            } else {
1410                source.consumed
1411            },
1412        }),
1413        Artifact::GdtAnnotation(source) => Artifact::GdtAnnotation(GdtAnnotationArtifact {
1414            id: remap_id_for_clone(source.id, entity_id_map),
1415            code_ref: clone_code_ref.clone(),
1416        }),
1417        Artifact::Pattern(source) => Artifact::Pattern(Pattern {
1418            id: remap_id_for_clone(source.id, entity_id_map),
1419            sub_type: source.sub_type,
1420            source_id: remap_id_for_clone(source.source_id, entity_id_map),
1421            copy_ids: remap_ids_for_clone(&source.copy_ids, entity_id_map),
1422            copy_face_ids: remap_ids_for_clone(&source.copy_face_ids, entity_id_map),
1423            copy_edge_ids: remap_ids_for_clone(&source.copy_edge_ids, entity_id_map),
1424            code_ref: clone_code_ref.clone(),
1425        }),
1426    }
1427}
1428
1429fn pattern_source_ids(artifacts: &IndexMap<ArtifactId, Artifact>, source_id: ArtifactId) -> Vec<ArtifactId> {
1430    let mut source_ids = vec![source_id];
1431
1432    if let Some(Artifact::Path(path)) = artifacts.get(&source_id) {
1433        if let Some(sweep_id) = path.sweep_id {
1434            source_ids.push(sweep_id);
1435        }
1436        if let Some(composite_solid_id) = path.composite_solid_id {
1437            source_ids.push(composite_solid_id);
1438        }
1439    }
1440
1441    for artifact in artifacts.values() {
1442        match artifact {
1443            Artifact::Sweep(sweep) if sweep.path_id == source_id => source_ids.push(sweep.id),
1444            Artifact::CompositeSolid(composite)
1445                if composite.solid_ids.contains(&source_id) || composite.tool_ids.contains(&source_id) =>
1446            {
1447                source_ids.push(composite.id)
1448            }
1449            _ => {}
1450        }
1451    }
1452
1453    let mut unique = Vec::new();
1454    merge_ids(&mut unique, source_ids);
1455    unique
1456}
1457
1458fn pattern_artifact_updates(
1459    artifacts: &IndexMap<ArtifactId, Artifact>,
1460    pattern_id: ArtifactId,
1461    sub_type: PatternSubType,
1462    source_id: ArtifactId,
1463    face_edge_infos: &[kcmc::output::FaceEdgeInfo],
1464    code_ref: CodeRef,
1465) -> Vec<Artifact> {
1466    let copy_ids = face_edge_infos
1467        .iter()
1468        .map(|info| ArtifactId::new(info.object_id))
1469        .collect::<Vec<_>>();
1470    let copy_face_ids = face_edge_infos
1471        .iter()
1472        .flat_map(|info| info.faces.iter().copied().map(ArtifactId::new))
1473        .collect::<Vec<_>>();
1474    let copy_edge_ids = face_edge_infos
1475        .iter()
1476        .flat_map(|info| info.edges.iter().copied().map(ArtifactId::new))
1477        .collect::<Vec<_>>();
1478
1479    let source_ids = pattern_source_ids(artifacts, source_id);
1480    let mut return_arr = vec![Artifact::Pattern(Pattern {
1481        id: pattern_id,
1482        sub_type,
1483        source_id,
1484        copy_ids,
1485        copy_face_ids,
1486        copy_edge_ids,
1487        code_ref,
1488    })];
1489
1490    for source_id in source_ids {
1491        let Some(artifact) = artifacts.get(&source_id) else {
1492            continue;
1493        };
1494        match artifact {
1495            Artifact::Path(path) => {
1496                let mut new_path = path.clone();
1497                new_path.pattern_ids = vec![pattern_id];
1498                return_arr.push(Artifact::Path(new_path));
1499            }
1500            Artifact::Sweep(sweep) => {
1501                let mut new_sweep = sweep.clone();
1502                new_sweep.pattern_ids = vec![pattern_id];
1503                return_arr.push(Artifact::Sweep(new_sweep));
1504            }
1505            Artifact::CompositeSolid(composite) => {
1506                let mut new_composite = composite.clone();
1507                new_composite.pattern_ids = vec![pattern_id];
1508                return_arr.push(Artifact::CompositeSolid(new_composite));
1509            }
1510            _ => {}
1511        }
1512    }
1513
1514    return_arr
1515}
1516
1517fn is_single_target_self_subtract(target_ids: &[Uuid], tool_ids: &[Uuid]) -> bool {
1518    target_ids.len() == 1 && tool_ids.len() == 1 && target_ids[0] == tool_ids[0]
1519}
1520
1521fn boolean_subtract_output_artifact_ids(
1522    cmd_id: ArtifactId,
1523    target_ids: &[Uuid],
1524    tool_ids: &[Uuid],
1525    extra_solid_ids: &[Uuid],
1526) -> Vec<ArtifactId> {
1527    if is_single_target_self_subtract(target_ids, tool_ids) {
1528        return Vec::new();
1529    }
1530
1531    let mut output_ids = if target_ids.len() == 1 {
1532        vec![cmd_id]
1533    } else {
1534        Vec::new()
1535    };
1536
1537    for extra_solid_id in extra_solid_ids {
1538        let artifact_id = ArtifactId::new(*extra_solid_id);
1539        if !output_ids.contains(&artifact_id) {
1540            output_ids.push(artifact_id);
1541        }
1542    }
1543
1544    output_ids
1545}
1546
1547fn update_consumed_csg_sweep(
1548    return_arr: &mut Vec<Artifact>,
1549    artifacts: &IndexMap<ArtifactId, Artifact>,
1550    sweep_id: ArtifactId,
1551    consumed_sweep_ids: &mut FnvHashSet<ArtifactId>,
1552) {
1553    if consumed_sweep_ids.insert(sweep_id)
1554        && let Some(Artifact::Sweep(sweep)) = artifacts.get(&sweep_id)
1555    {
1556        let mut new_sweep = sweep.clone();
1557        new_sweep.consumed = true;
1558        return_arr.push(Artifact::Sweep(new_sweep));
1559    }
1560}
1561
1562fn update_csg_input_artifacts(
1563    return_arr: &mut Vec<Artifact>,
1564    artifacts: &IndexMap<ArtifactId, Artifact>,
1565    input_ids: &[ArtifactId],
1566    composite_solid_id: Option<ArtifactId>,
1567    consumed_sweep_ids: &mut FnvHashSet<ArtifactId>,
1568) {
1569    for input_id in input_ids {
1570        if let Some(artifact) = artifacts.get(input_id) {
1571            match artifact {
1572                Artifact::CompositeSolid(comp) => {
1573                    let mut new_comp = comp.clone();
1574                    new_comp.composite_solid_id = composite_solid_id;
1575                    new_comp.consumed = true;
1576                    return_arr.push(Artifact::CompositeSolid(new_comp));
1577                }
1578                Artifact::Path(path) => {
1579                    let mut new_path = path.clone();
1580                    new_path.composite_solid_id = composite_solid_id;
1581
1582                    // We want to mark any sweeps of the path used in this operation
1583                    // as consumed. The path itself is already consumed by sweeping.
1584                    if let Some(sweep_id) = new_path.sweep_id {
1585                        update_consumed_csg_sweep(return_arr, artifacts, sweep_id, consumed_sweep_ids);
1586                    }
1587
1588                    return_arr.push(Artifact::Path(new_path));
1589                }
1590                Artifact::Sweep(sweep) => {
1591                    update_consumed_csg_sweep(return_arr, artifacts, sweep.id, consumed_sweep_ids);
1592                }
1593                _ => {}
1594            }
1595        }
1596    }
1597}
1598
1599fn mirror_3d_artifact_updates(
1600    artifacts: &IndexMap<ArtifactId, Artifact>,
1601    original_solid_ids: &[Uuid],
1602    face_edge_infos: &[kcmc::output::FaceEdgeInfo],
1603    code_ref: CodeRef,
1604    range: SourceRange,
1605    cmd: &ModelingCmd,
1606) -> Result<Vec<Artifact>, KclError> {
1607    if original_solid_ids.len() != face_edge_infos.len() {
1608        internal_error!(
1609            range,
1610            "EntityMirrorAcross response has different number face edge info than original mirrored solids: cmd={cmd:?}, face_edge_infos={face_edge_infos:?}"
1611        );
1612    }
1613
1614    let mut return_arr = Vec::new();
1615    for (face_edge_info, original_solid_id) in face_edge_infos.iter().zip(original_solid_ids) {
1616        let original_solid_id = ArtifactId::new(*original_solid_id);
1617        let mirrored_solid_id = ArtifactId::new(face_edge_info.object_id);
1618        let source_solid = match artifacts.get(&original_solid_id) {
1619            Some(Artifact::Path(path)) => path.sweep_id.and_then(|sweep_id| artifacts.get(&sweep_id)).or_else(|| {
1620                path.composite_solid_id
1621                    .and_then(|composite_id| artifacts.get(&composite_id))
1622            }),
1623            source => source,
1624        };
1625        match source_solid {
1626            Some(Artifact::Sweep(sweep)) => {
1627                let mut mirrored_sweep = sweep.clone();
1628                mirrored_sweep.id = mirrored_solid_id;
1629                mirrored_sweep.surface_ids = face_edge_info.faces.iter().copied().map(ArtifactId::new).collect();
1630                mirrored_sweep.edge_ids = face_edge_info.edges.iter().copied().map(ArtifactId::new).collect();
1631                mirrored_sweep.code_ref = code_ref.clone();
1632                mirrored_sweep.consumed = false;
1633                mirrored_sweep.pattern_ids = Vec::new();
1634                return_arr.push(Artifact::Sweep(mirrored_sweep));
1635            }
1636            Some(Artifact::CompositeSolid(composite)) => {
1637                let mut mirrored_composite = composite.clone();
1638                mirrored_composite.id = mirrored_solid_id;
1639                mirrored_composite.code_ref = code_ref.clone();
1640                mirrored_composite.consumed = false;
1641                mirrored_composite.composite_solid_id = None;
1642                mirrored_composite.pattern_ids = Vec::new();
1643                return_arr.push(Artifact::CompositeSolid(mirrored_composite));
1644            }
1645            Some(_) | None => continue,
1646        }
1647    }
1648
1649    Ok(return_arr)
1650}
1651
1652#[allow(clippy::too_many_arguments)]
1653fn artifacts_to_update(
1654    artifacts: &IndexMap<ArtifactId, Artifact>,
1655    artifact_command: &ArtifactCommand,
1656    responses: &FnvHashMap<Uuid, OkModelingCmdResponse>,
1657    entity_clone_id_maps: &FnvHashMap<Uuid, FnvHashMap<ArtifactId, ArtifactId>>,
1658    path_to_plane_id_map: &FnvHashMap<Uuid, Uuid>,
1659    programs: &crate::execution::ProgramLookup,
1660    cached_body_items: usize,
1661    exec_artifacts: &IndexMap<ArtifactId, Artifact>,
1662    import_code_refs: &FnvHashMap<ModuleId, ImportCodeRef>,
1663) -> Result<Vec<Artifact>, KclError> {
1664    let uuid = artifact_command.cmd_id;
1665    let response = responses.get(&uuid);
1666
1667    // TODO: Build path-to-node from artifact_command source range.  Right now,
1668    // we're serializing an empty array, and the TS wrapper fills it in with the
1669    // correct value based on NodePath.
1670    let path_to_node = Vec::new();
1671    let range = artifact_command.range;
1672    let (code_ref_range, node_path) = code_ref_for_range(programs, cached_body_items, range, import_code_refs);
1673    let code_ref = CodeRef {
1674        range: code_ref_range,
1675        node_path,
1676        path_to_node,
1677    };
1678
1679    let id = ArtifactId::new(uuid);
1680    let cmd = &artifact_command.command;
1681
1682    match cmd {
1683        ModelingCmd::MakePlane(_) => {
1684            if range.is_synthetic() {
1685                return Ok(Vec::new());
1686            }
1687            // If we're calling `make_plane` and the code range doesn't end at
1688            // `0` it's not a default plane, but a custom one from the
1689            // offsetPlane standard library function.
1690            return Ok(vec![Artifact::Plane(Plane {
1691                id,
1692                path_ids: Vec::new(),
1693                code_ref,
1694            })]);
1695        }
1696        ModelingCmd::FaceIsPlanar(FaceIsPlanar { object_id, .. }) => {
1697            return Ok(vec![Artifact::PlaneOfFace(PlaneOfFace {
1698                id,
1699                face_id: object_id.into(),
1700                code_ref,
1701            })]);
1702        }
1703        ModelingCmd::EnableSketchMode(EnableSketchMode { entity_id, .. }) => {
1704            let existing_plane = artifacts.get(&ArtifactId::new(*entity_id));
1705            match existing_plane {
1706                Some(Artifact::Wall(wall)) => {
1707                    return Ok(vec![Artifact::Wall(Wall {
1708                        id: entity_id.into(),
1709                        seg_id: wall.seg_id,
1710                        edge_cut_edge_ids: wall.edge_cut_edge_ids.clone(),
1711                        sweep_id: wall.sweep_id,
1712                        path_ids: wall.path_ids.clone(),
1713                        face_code_ref: wall.face_code_ref.clone(),
1714                        cmd_id: artifact_command.cmd_id,
1715                    })]);
1716                }
1717                Some(Artifact::Cap(cap)) => {
1718                    return Ok(vec![Artifact::Cap(Cap {
1719                        id: entity_id.into(),
1720                        sub_type: cap.sub_type,
1721                        edge_cut_edge_ids: cap.edge_cut_edge_ids.clone(),
1722                        sweep_id: cap.sweep_id,
1723                        path_ids: cap.path_ids.clone(),
1724                        face_code_ref: cap.face_code_ref.clone(),
1725                        cmd_id: artifact_command.cmd_id,
1726                    })]);
1727                }
1728                Some(_) | None => {
1729                    let path_ids = match existing_plane {
1730                        Some(Artifact::Plane(Plane { path_ids, .. })) => path_ids.clone(),
1731                        _ => Vec::new(),
1732                    };
1733                    // Create an entirely new plane
1734                    return Ok(vec![Artifact::Plane(Plane {
1735                        id: entity_id.into(),
1736                        path_ids,
1737                        code_ref,
1738                    })]);
1739                }
1740            }
1741        }
1742        ModelingCmd::StartPath(_) => {
1743            let mut return_arr = Vec::new();
1744            let current_plane_id = path_to_plane_id_map.get(&artifact_command.cmd_id).ok_or_else(|| {
1745                KclError::new_internal(KclErrorDetails::new(
1746                    format!("Expected a current plane ID when processing StartPath command, but we have none: {id:?}"),
1747                    vec![range],
1748                ))
1749            })?;
1750            let sketch_block_id = exec_artifacts
1751                .values()
1752                .find(|a| {
1753                    if let Artifact::SketchBlock(s) = a {
1754                        if let Some(path_id) = s.path_id {
1755                            path_id == id
1756                        } else {
1757                            false
1758                        }
1759                    } else {
1760                        false
1761                    }
1762                })
1763                .map(|a| a.id());
1764            return_arr.push(Artifact::Path(Path {
1765                id,
1766                sub_type: PathSubType::Sketch,
1767                plane_id: (*current_plane_id).into(),
1768                seg_ids: Vec::new(),
1769                sweep_id: None,
1770                trajectory_sweep_id: None,
1771                solid2d_id: None,
1772                code_ref,
1773                composite_solid_id: None,
1774                sketch_block_id,
1775                origin_path_id: None,
1776                inner_path_id: None,
1777                outer_path_id: None,
1778                pattern_ids: Vec::new(),
1779                consumed: false,
1780            }));
1781            let plane = artifacts.get(&ArtifactId::new(*current_plane_id));
1782            if let Some(Artifact::Plane(plane)) = plane {
1783                let plane_code_ref = plane.code_ref.clone();
1784                return_arr.push(Artifact::Plane(Plane {
1785                    id: (*current_plane_id).into(),
1786                    path_ids: vec![id],
1787                    code_ref: plane_code_ref,
1788                }));
1789            }
1790            if let Some(Artifact::Wall(wall)) = plane {
1791                return_arr.push(Artifact::Wall(Wall {
1792                    id: (*current_plane_id).into(),
1793                    seg_id: wall.seg_id,
1794                    edge_cut_edge_ids: wall.edge_cut_edge_ids.clone(),
1795                    sweep_id: wall.sweep_id,
1796                    path_ids: vec![id],
1797                    face_code_ref: wall.face_code_ref.clone(),
1798                    cmd_id: artifact_command.cmd_id,
1799                }));
1800            }
1801            if let Some(Artifact::Cap(cap)) = plane {
1802                return_arr.push(Artifact::Cap(Cap {
1803                    id: (*current_plane_id).into(),
1804                    sub_type: cap.sub_type,
1805                    edge_cut_edge_ids: cap.edge_cut_edge_ids.clone(),
1806                    sweep_id: cap.sweep_id,
1807                    path_ids: vec![id],
1808                    face_code_ref: cap.face_code_ref.clone(),
1809                    cmd_id: artifact_command.cmd_id,
1810                }));
1811            }
1812            return Ok(return_arr);
1813        }
1814        ModelingCmd::ClosePath(_) | ModelingCmd::ExtendPath(_) => {
1815            let path_id = ArtifactId::new(match cmd {
1816                ModelingCmd::ClosePath(c) => c.path_id,
1817                ModelingCmd::ExtendPath(e) => e.path.into(),
1818                _ => internal_error!(
1819                    range,
1820                    "Close or extend path command variant not handled: id={id:?}, cmd={cmd:?}"
1821                ),
1822            });
1823            let mut return_arr = Vec::new();
1824            return_arr.push(Artifact::Segment(Segment {
1825                id,
1826                path_id,
1827                original_seg_id: None,
1828                surface_id: None,
1829                edge_ids: Vec::new(),
1830                edge_cut_id: None,
1831                code_ref,
1832                common_surface_ids: Vec::new(),
1833            }));
1834            let path = artifacts.get(&path_id);
1835            if let Some(Artifact::Path(path)) = path {
1836                let mut new_path = path.clone();
1837                new_path.seg_ids = vec![id];
1838                return_arr.push(Artifact::Path(new_path));
1839            }
1840            if let Some(OkModelingCmdResponse::ClosePath(close_path)) = response {
1841                return_arr.push(Artifact::Solid2d(Solid2d {
1842                    id: close_path.face_id.into(),
1843                    path_id,
1844                }));
1845                if let Some(Artifact::Path(path)) = path {
1846                    let mut new_path = path.clone();
1847                    new_path.solid2d_id = Some(close_path.face_id.into());
1848                    return_arr.push(Artifact::Path(new_path));
1849                }
1850            }
1851            return Ok(return_arr);
1852        }
1853        ModelingCmd::CreateRegion(kcmc::CreateRegion {
1854            object_id: origin_path_id,
1855            ..
1856        })
1857        | ModelingCmd::CreateRegionFromQueryPoint(kcmc::CreateRegionFromQueryPoint {
1858            object_id: origin_path_id,
1859            ..
1860        }) => {
1861            let mut return_arr = Vec::new();
1862            let origin_path = artifacts.get(&ArtifactId::new(*origin_path_id));
1863            let Some(Artifact::Path(path)) = origin_path else {
1864                internal_error!(
1865                    range,
1866                    "Expected to find an existing path for the origin path of CreateRegion or CreateRegionFromQueryPoint command, but found none: origin_path={origin_path:?}, cmd={cmd:?}"
1867                );
1868            };
1869            // Create the path representing the region.
1870            return_arr.push(Artifact::Path(Path {
1871                id,
1872                sub_type: PathSubType::Region,
1873                plane_id: path.plane_id,
1874                seg_ids: Vec::new(),
1875                consumed: false,
1876                sweep_id: None,
1877                trajectory_sweep_id: None,
1878                solid2d_id: None,
1879                code_ref: code_ref.clone(),
1880                composite_solid_id: None,
1881                sketch_block_id: None,
1882                origin_path_id: Some(ArtifactId::new(*origin_path_id)),
1883                inner_path_id: None,
1884                outer_path_id: None,
1885                pattern_ids: Vec::new(),
1886            }));
1887            // If we have a response, we can also create the segments in the
1888            // region.
1889            let Some(
1890                OkModelingCmdResponse::CreateRegion(kcmc::output::CreateRegion { region_mapping, .. })
1891                | OkModelingCmdResponse::CreateRegionFromQueryPoint(kcmc::output::CreateRegionFromQueryPoint {
1892                    region_mapping,
1893                    ..
1894                }),
1895            ) = response
1896            else {
1897                return Ok(return_arr);
1898            };
1899            // Each key is a segment in the region. The value is the segment in
1900            // the original path. Build the reverse mapping.
1901            let original_segment_ids = path.seg_ids.iter().map(|p| p.0).collect::<Vec<_>>();
1902            let reverse = build_reverse_region_mapping(region_mapping, &original_segment_ids);
1903            for (original_segment_id, region_segment_ids) in reverse.iter() {
1904                for segment_id in region_segment_ids {
1905                    return_arr.push(Artifact::Segment(Segment {
1906                        id: ArtifactId::new(*segment_id),
1907                        path_id: id,
1908                        original_seg_id: Some(ArtifactId::new(*original_segment_id)),
1909                        surface_id: None,
1910                        edge_ids: Vec::new(),
1911                        edge_cut_id: None,
1912                        code_ref: code_ref.clone(),
1913                        common_surface_ids: Vec::new(),
1914                    }))
1915                }
1916            }
1917            return Ok(return_arr);
1918        }
1919        ModelingCmd::Solid3dGetFaceUuid(kcmc::Solid3dGetFaceUuid { object_id, .. }) => {
1920            let Some(OkModelingCmdResponse::Solid3dGetFaceUuid(face_uuid)) = response else {
1921                return Ok(Vec::new());
1922            };
1923
1924            return Ok(vec![Artifact::PrimitiveFace(PrimitiveFace {
1925                id: face_uuid.face_id.into(),
1926                solid_id: (*object_id).into(),
1927                code_ref,
1928            })]);
1929        }
1930        ModelingCmd::Solid3dGetEdgeUuid(kcmc::Solid3dGetEdgeUuid { object_id, .. }) => {
1931            let Some(OkModelingCmdResponse::Solid3dGetEdgeUuid(edge_uuid)) = response else {
1932                return Ok(Vec::new());
1933            };
1934
1935            return Ok(vec![Artifact::PrimitiveEdge(PrimitiveEdge {
1936                id: edge_uuid.edge_id.into(),
1937                solid_id: (*object_id).into(),
1938                code_ref,
1939            })]);
1940        }
1941        ModelingCmd::EntityLinearPatternTransform(pattern_cmd) => {
1942            let face_edge_infos = match response {
1943                Some(OkModelingCmdResponse::EntityLinearPatternTransform(resp)) => resp.entity_face_edge_ids.as_slice(),
1944                _ => &[],
1945            };
1946            return Ok(pattern_artifact_updates(
1947                artifacts,
1948                id,
1949                PatternSubType::Transform,
1950                ArtifactId::new(pattern_cmd.entity_id),
1951                face_edge_infos,
1952                code_ref,
1953            ));
1954        }
1955        ModelingCmd::EntityLinearPattern(pattern_cmd) => {
1956            let face_edge_infos = match response {
1957                Some(OkModelingCmdResponse::EntityLinearPattern(resp)) => resp.entity_face_edge_ids.as_slice(),
1958                _ => &[],
1959            };
1960            return Ok(pattern_artifact_updates(
1961                artifacts,
1962                id,
1963                PatternSubType::Linear,
1964                ArtifactId::new(pattern_cmd.entity_id),
1965                face_edge_infos,
1966                code_ref,
1967            ));
1968        }
1969        ModelingCmd::EntityCircularPattern(pattern_cmd) => {
1970            let face_edge_infos = match response {
1971                Some(OkModelingCmdResponse::EntityCircularPattern(resp)) => resp.entity_face_edge_ids.as_slice(),
1972                _ => &[],
1973            };
1974            return Ok(pattern_artifact_updates(
1975                artifacts,
1976                id,
1977                PatternSubType::Circular,
1978                ArtifactId::new(pattern_cmd.entity_id),
1979                face_edge_infos,
1980                code_ref,
1981            ));
1982        }
1983        ModelingCmd::EntityMirrorAcross(kcmc::EntityMirrorAcross {
1984            ids: original_solid_ids,
1985            ..
1986        }) => {
1987            let face_edge_infos = match response {
1988                Some(OkModelingCmdResponse::EntityMirrorAcross(resp)) => resp.entity_face_edge_ids.as_slice(),
1989                _ => internal_error!(
1990                    range,
1991                    "EntityMirrorAcross response variant not handled: id={id:?}, cmd={cmd:?}, response={response:?}"
1992                ),
1993            };
1994            return mirror_3d_artifact_updates(artifacts, original_solid_ids, face_edge_infos, code_ref, range, cmd);
1995        }
1996        ModelingCmd::EntityMirror(kcmc::EntityMirror {
1997            ids: original_path_ids, ..
1998        })
1999        | ModelingCmd::EntityMirrorAcrossEdge(kcmc::EntityMirrorAcrossEdge {
2000            ids: original_path_ids, ..
2001        }) => {
2002            let face_edge_infos = match response {
2003                Some(OkModelingCmdResponse::EntityMirror(resp)) => &resp.entity_face_edge_ids,
2004                Some(OkModelingCmdResponse::EntityMirrorAcrossEdge(resp)) => &resp.entity_face_edge_ids,
2005                _ => internal_error!(
2006                    range,
2007                    "Mirror response variant not handled: id={id:?}, cmd={cmd:?}, response={response:?}"
2008                ),
2009            };
2010            if original_path_ids.len() != face_edge_infos.len() {
2011                internal_error!(
2012                    range,
2013                    "EntityMirror or EntityMirrorAcrossEdge response has different number face edge info than original mirrored paths: id={id:?}, cmd={cmd:?}, response={response:?}"
2014                );
2015            }
2016            let mut return_arr = Vec::new();
2017            for (face_edge_info, original_path_id) in face_edge_infos.iter().zip(original_path_ids) {
2018                let original_path_id = ArtifactId::new(*original_path_id);
2019                let path_id = ArtifactId::new(face_edge_info.object_id);
2020                // The path may be an existing path that was extended or a new
2021                // path.
2022                let mut path = if let Some(Artifact::Path(path)) = artifacts.get(&path_id) {
2023                    // Existing path.
2024                    path.clone()
2025                } else {
2026                    // It's a new path.  We need the original path to get some
2027                    // of its info.
2028                    let Some(Artifact::Path(original_path)) = artifacts.get(&original_path_id) else {
2029                        // We couldn't find the original path. This is a bug.
2030                        internal_error!(
2031                            range,
2032                            "Couldn't find original path for mirror2d: original_path_id={original_path_id:?}, cmd={cmd:?}"
2033                        );
2034                    };
2035                    Path {
2036                        id: path_id,
2037                        sub_type: original_path.sub_type,
2038                        plane_id: original_path.plane_id,
2039                        seg_ids: Vec::new(),
2040                        sweep_id: None,
2041                        trajectory_sweep_id: None,
2042                        solid2d_id: None,
2043                        code_ref: code_ref.clone(),
2044                        composite_solid_id: None,
2045                        sketch_block_id: None,
2046                        origin_path_id: original_path.origin_path_id,
2047                        inner_path_id: None,
2048                        outer_path_id: None,
2049                        pattern_ids: Vec::new(),
2050                        consumed: false,
2051                    }
2052                };
2053
2054                face_edge_info.edges.iter().for_each(|edge_id| {
2055                    let edge_id = ArtifactId::new(*edge_id);
2056                    return_arr.push(Artifact::Segment(Segment {
2057                        id: edge_id,
2058                        path_id: path.id,
2059                        original_seg_id: None,
2060                        surface_id: None,
2061                        edge_ids: Vec::new(),
2062                        edge_cut_id: None,
2063                        code_ref: code_ref.clone(),
2064                        common_surface_ids: Vec::new(),
2065                    }));
2066                    // Add the edge ID to the path.
2067                    path.seg_ids.push(edge_id);
2068                });
2069
2070                return_arr.push(Artifact::Path(path));
2071            }
2072            return Ok(return_arr);
2073        }
2074        ModelingCmd::EntityClone(kcmc::EntityClone { entity_id, .. }) => {
2075            let source_id = ArtifactId::new(*entity_id);
2076
2077            let Some(source_artifact) = artifacts.get(&source_id) else {
2078                return Ok(Vec::new());
2079            };
2080
2081            let mut entity_id_map = entity_clone_id_maps.get(&uuid).cloned().unwrap_or_default();
2082            entity_id_map.insert(source_id, id);
2083
2084            let mut cloned_artifacts = Vec::new();
2085            cloned_artifacts.push(remap_artifact_for_clone(
2086                source_artifact,
2087                &entity_id_map,
2088                &code_ref,
2089                artifact_command.cmd_id,
2090                source_id,
2091            ));
2092
2093            for artifact in artifacts.values() {
2094                let artifact_id = artifact.id();
2095                if artifact_id == source_id || !entity_id_map.contains_key(&artifact_id) {
2096                    continue;
2097                }
2098                cloned_artifacts.push(remap_artifact_for_clone(
2099                    artifact,
2100                    &entity_id_map,
2101                    &code_ref,
2102                    artifact_command.cmd_id,
2103                    source_id,
2104                ));
2105            }
2106
2107            return Ok(cloned_artifacts);
2108        }
2109        ModelingCmd::Extrude(kcmc::Extrude { target, .. })
2110        | ModelingCmd::TwistExtrude(kcmc::TwistExtrude { target, .. })
2111        | ModelingCmd::Revolve(kcmc::Revolve { target, .. })
2112        | ModelingCmd::RevolveAboutEdge(kcmc::RevolveAboutEdge { target, .. })
2113        | ModelingCmd::ExtrudeToReference(kcmc::ExtrudeToReference { target, .. }) => {
2114            // Determine the resulting method from the specific command, if provided
2115            let method = match cmd {
2116                ModelingCmd::Extrude(kcmc::Extrude { extrude_method, .. }) => *extrude_method,
2117                ModelingCmd::ExtrudeToReference(kcmc::ExtrudeToReference { extrude_method, .. }) => *extrude_method,
2118                // TwistExtrude and Sweep don't carry method in the command; treat as Merge
2119                ModelingCmd::TwistExtrude(_) | ModelingCmd::Sweep(_) => {
2120                    kittycad_modeling_cmds::shared::ExtrudeMethod::Merge
2121                }
2122                // Revolve variants behave like New bodies in std layer
2123                ModelingCmd::Revolve(_) | ModelingCmd::RevolveAboutEdge(_) => {
2124                    kittycad_modeling_cmds::shared::ExtrudeMethod::New
2125                }
2126                _ => kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
2127            };
2128            let sub_type = match cmd {
2129                ModelingCmd::Extrude(_) => SweepSubType::Extrusion,
2130                ModelingCmd::ExtrudeToReference(_) => SweepSubType::Extrusion,
2131                ModelingCmd::TwistExtrude(_) => SweepSubType::ExtrusionTwist,
2132                ModelingCmd::Revolve(_) => SweepSubType::Revolve,
2133                ModelingCmd::RevolveAboutEdge(_) => SweepSubType::RevolveAboutEdge,
2134                _ => internal_error!(range, "Sweep-like command variant not handled: id={id:?}, cmd={cmd:?}",),
2135            };
2136            let mut return_arr = Vec::new();
2137            let target = ArtifactId::from(target);
2138            return_arr.push(Artifact::Sweep(Sweep {
2139                id,
2140                sub_type,
2141                path_id: target,
2142                surface_ids: Vec::new(),
2143                edge_ids: Vec::new(),
2144                code_ref,
2145                trajectory_id: None,
2146                method,
2147                consumed: false,
2148                pattern_ids: Vec::new(),
2149            }));
2150            let path = artifacts.get(&target);
2151            if let Some(Artifact::Path(path)) = path {
2152                let mut new_path = path.clone();
2153                new_path.sweep_id = Some(id);
2154                new_path.consumed = true;
2155                return_arr.push(Artifact::Path(new_path));
2156                if let Some(inner_path_id) = path.inner_path_id
2157                    && let Some(inner_path_artifact) = artifacts.get(&inner_path_id)
2158                    && let Artifact::Path(mut inner_path_artifact) = inner_path_artifact.clone()
2159                {
2160                    inner_path_artifact.sweep_id = Some(id);
2161                    inner_path_artifact.consumed = true;
2162                    return_arr.push(Artifact::Path(inner_path_artifact))
2163                }
2164            }
2165            return Ok(return_arr);
2166        }
2167        ModelingCmd::Sweep(kcmc::Sweep { target, trajectory, .. }) => {
2168            // Determine the resulting method from the specific command, if provided
2169            let method = kittycad_modeling_cmds::shared::ExtrudeMethod::Merge;
2170            let sub_type = SweepSubType::Sweep;
2171            let mut return_arr = Vec::new();
2172            let target = ArtifactId::from(target);
2173            let trajectory = ArtifactId::from(trajectory);
2174            return_arr.push(Artifact::Sweep(Sweep {
2175                id,
2176                sub_type,
2177                path_id: target,
2178                surface_ids: Vec::new(),
2179                edge_ids: Vec::new(),
2180                code_ref,
2181                trajectory_id: Some(trajectory),
2182                method,
2183                consumed: false,
2184                pattern_ids: Vec::new(),
2185            }));
2186            let path = artifacts.get(&target);
2187            if let Some(Artifact::Path(path)) = path {
2188                let mut new_path = path.clone();
2189                new_path.sweep_id = Some(id);
2190                new_path.consumed = true;
2191                return_arr.push(Artifact::Path(new_path));
2192                if let Some(inner_path_id) = path.inner_path_id
2193                    && let Some(inner_path_artifact) = artifacts.get(&inner_path_id)
2194                    && let Artifact::Path(mut inner_path_artifact) = inner_path_artifact.clone()
2195                {
2196                    inner_path_artifact.sweep_id = Some(id);
2197                    inner_path_artifact.consumed = true;
2198                    return_arr.push(Artifact::Path(inner_path_artifact))
2199                }
2200            }
2201            if let Some(trajectory_artifact) = artifacts.get(&trajectory) {
2202                match trajectory_artifact {
2203                    Artifact::Path(path) => {
2204                        let mut new_path = path.clone();
2205                        new_path.trajectory_sweep_id = Some(id);
2206                        new_path.consumed = true;
2207                        return_arr.push(Artifact::Path(new_path));
2208                    }
2209                    Artifact::Helix(helix) => {
2210                        let mut new_helix = helix.clone();
2211                        new_helix.trajectory_sweep_id = Some(id);
2212                        new_helix.consumed = true;
2213                        return_arr.push(Artifact::Helix(new_helix));
2214                    }
2215                    _ => {}
2216                }
2217            };
2218            return Ok(return_arr);
2219        }
2220        ModelingCmd::SurfaceBlend(surface_blend_cmd) => {
2221            let surface_id_to_path_id = |surface_id: ArtifactId| -> Option<ArtifactId> {
2222                match artifacts.get(&surface_id) {
2223                    Some(Artifact::Path(path)) => Some(path.id),
2224                    Some(Artifact::Segment(segment)) => Some(segment.path_id),
2225                    Some(Artifact::Sweep(sweep)) => Some(sweep.path_id),
2226                    Some(Artifact::Wall(wall)) => artifacts.get(&wall.sweep_id).and_then(|artifact| match artifact {
2227                        Artifact::Sweep(sweep) => Some(sweep.path_id),
2228                        _ => None,
2229                    }),
2230                    Some(Artifact::Cap(cap)) => artifacts.get(&cap.sweep_id).and_then(|artifact| match artifact {
2231                        Artifact::Sweep(sweep) => Some(sweep.path_id),
2232                        _ => None,
2233                    }),
2234                    _ => None,
2235                }
2236            };
2237            let Some(first_surface_ref) = surface_blend_cmd.surfaces.first() else {
2238                internal_error!(range, "SurfaceBlend command has no surfaces: id={id:?}, cmd={cmd:?}");
2239            };
2240            let first_surface_id = ArtifactId::new(first_surface_ref.object_id);
2241            let path_id = surface_id_to_path_id(first_surface_id).unwrap_or(first_surface_id);
2242            let trajectory_id = surface_blend_cmd
2243                .surfaces
2244                .get(1)
2245                .map(|surface| ArtifactId::new(surface.object_id))
2246                .and_then(surface_id_to_path_id);
2247            let return_arr = vec![Artifact::Sweep(Sweep {
2248                id,
2249                sub_type: SweepSubType::Blend,
2250                path_id,
2251                surface_ids: Vec::new(),
2252                edge_ids: Vec::new(),
2253                code_ref,
2254                trajectory_id,
2255                method: kittycad_modeling_cmds::shared::ExtrudeMethod::New,
2256                consumed: false,
2257                pattern_ids: Vec::new(),
2258            })];
2259            return Ok(return_arr);
2260        }
2261        ModelingCmd::Loft(loft_cmd) => {
2262            let Some(OkModelingCmdResponse::Loft(_)) = response else {
2263                return Ok(Vec::new());
2264            };
2265            let mut return_arr = Vec::new();
2266            return_arr.push(Artifact::Sweep(Sweep {
2267                id,
2268                sub_type: SweepSubType::Loft,
2269                // TODO: Using the first one.  Make sure to revisit this
2270                // choice, don't think it matters for now.
2271                path_id: ArtifactId::new(*loft_cmd.section_ids.first().ok_or_else(|| {
2272                    KclError::new_internal(KclErrorDetails::new(
2273                        format!("Expected at least one section ID in Loft command: {id:?}; cmd={cmd:?}"),
2274                        vec![range],
2275                    ))
2276                })?),
2277                surface_ids: Vec::new(),
2278                edge_ids: Vec::new(),
2279                code_ref,
2280                trajectory_id: None,
2281                method: kittycad_modeling_cmds::shared::ExtrudeMethod::Merge,
2282                consumed: false,
2283                pattern_ids: Vec::new(),
2284            }));
2285            for section_id in &loft_cmd.section_ids {
2286                let path = artifacts.get(&ArtifactId::new(*section_id));
2287                if let Some(Artifact::Path(path)) = path {
2288                    let mut new_path = path.clone();
2289                    new_path.consumed = true;
2290                    new_path.sweep_id = Some(id);
2291                    return_arr.push(Artifact::Path(new_path));
2292                }
2293            }
2294            return Ok(return_arr);
2295        }
2296        ModelingCmd::Solid3dGetExtrusionFaceInfo(_) => {
2297            let Some(OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(face_info)) = response else {
2298                return Ok(Vec::new());
2299            };
2300            let mut return_arr = Vec::new();
2301            let mut last_path = None;
2302            for face in &face_info.faces {
2303                if face.cap != ExtrusionFaceCapType::None {
2304                    continue;
2305                }
2306                let Some(curve_id) = face.curve_id.map(ArtifactId::new) else {
2307                    continue;
2308                };
2309                let Some(face_id) = face.face_id.map(ArtifactId::new) else {
2310                    continue;
2311                };
2312                let Some(Artifact::Segment(seg)) = artifacts.get(&curve_id) else {
2313                    continue;
2314                };
2315                let Some(Artifact::Path(path)) = artifacts.get(&seg.path_id) else {
2316                    continue;
2317                };
2318                last_path = Some(path);
2319                let Some(path_sweep_id) = path.sweep_id else {
2320                    // If the path doesn't have a sweep ID, check if it's a
2321                    // hole.
2322                    if path.outer_path_id.is_some() {
2323                        continue; // hole not handled
2324                    }
2325                    return Err(KclError::new_internal(KclErrorDetails::new(
2326                        format!(
2327                            "Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none:\n{id:#?}\n{path:#?}"
2328                        ),
2329                        vec![range],
2330                    )));
2331                };
2332                let extra_artifact = exec_artifacts.values().find(|a| {
2333                    if let Artifact::StartSketchOnFace(s) = a {
2334                        s.face_id == face_id
2335                    } else if let Artifact::StartSketchOnPlane(s) = a {
2336                        s.plane_id == face_id
2337                    } else {
2338                        false
2339                    }
2340                });
2341                let sketch_on_face_code_ref = extra_artifact
2342                    .and_then(|a| match a {
2343                        Artifact::StartSketchOnFace(s) => Some(s.code_ref.clone()),
2344                        Artifact::StartSketchOnPlane(s) => Some(s.code_ref.clone()),
2345                        _ => None,
2346                    })
2347                    // TODO: If we didn't find it, it's probably a bug.
2348                    .unwrap_or_default();
2349
2350                return_arr.push(Artifact::Wall(Wall {
2351                    id: face_id,
2352                    seg_id: curve_id,
2353                    edge_cut_edge_ids: Vec::new(),
2354                    sweep_id: path_sweep_id,
2355                    path_ids: Vec::new(),
2356                    face_code_ref: sketch_on_face_code_ref,
2357                    cmd_id: artifact_command.cmd_id,
2358                }));
2359                let mut new_seg = seg.clone();
2360                new_seg.surface_id = Some(face_id);
2361                return_arr.push(Artifact::Segment(new_seg));
2362                if let Some(Artifact::Sweep(sweep)) = path.sweep_id.and_then(|id| artifacts.get(&id)) {
2363                    let mut new_sweep = sweep.clone();
2364                    new_sweep.surface_ids = vec![face_id];
2365                    return_arr.push(Artifact::Sweep(new_sweep));
2366                }
2367            }
2368            if let Some(path) = last_path {
2369                for face in &face_info.faces {
2370                    let sub_type = match face.cap {
2371                        ExtrusionFaceCapType::Top => CapSubType::End,
2372                        ExtrusionFaceCapType::Bottom => CapSubType::Start,
2373                        ExtrusionFaceCapType::None | ExtrusionFaceCapType::Both => continue,
2374                        _other => {
2375                            // Modeling API has added something we're not aware of.
2376                            continue;
2377                        }
2378                    };
2379                    let Some(face_id) = face.face_id.map(ArtifactId::new) else {
2380                        continue;
2381                    };
2382                    let Some(path_sweep_id) = path.sweep_id else {
2383                        // If the path doesn't have a sweep ID, check if it's a
2384                        // hole.
2385                        if path.outer_path_id.is_some() {
2386                            continue; // hole not handled
2387                        }
2388                        return Err(KclError::new_internal(KclErrorDetails::new(
2389                            format!(
2390                                "Expected a sweep ID on the path when processing last path's Solid3dGetExtrusionFaceInfo command, but we have none:\n{id:#?}\n{path:#?}"
2391                            ),
2392                            vec![range],
2393                        )));
2394                    };
2395                    let extra_artifact = exec_artifacts.values().find(|a| {
2396                        if let Artifact::StartSketchOnFace(s) = a {
2397                            s.face_id == face_id
2398                        } else if let Artifact::StartSketchOnPlane(s) = a {
2399                            s.plane_id == face_id
2400                        } else {
2401                            false
2402                        }
2403                    });
2404                    let sketch_on_face_code_ref = extra_artifact
2405                        .and_then(|a| match a {
2406                            Artifact::StartSketchOnFace(s) => Some(s.code_ref.clone()),
2407                            Artifact::StartSketchOnPlane(s) => Some(s.code_ref.clone()),
2408                            _ => None,
2409                        })
2410                        // TODO: If we didn't find it, it's probably a bug.
2411                        .unwrap_or_default();
2412                    return_arr.push(Artifact::Cap(Cap {
2413                        id: face_id,
2414                        sub_type,
2415                        edge_cut_edge_ids: Vec::new(),
2416                        sweep_id: path_sweep_id,
2417                        path_ids: Vec::new(),
2418                        face_code_ref: sketch_on_face_code_ref,
2419                        cmd_id: artifact_command.cmd_id,
2420                    }));
2421                    let Some(Artifact::Sweep(sweep)) = artifacts.get(&path_sweep_id) else {
2422                        continue;
2423                    };
2424                    let mut new_sweep = sweep.clone();
2425                    new_sweep.surface_ids = vec![face_id];
2426                    return_arr.push(Artifact::Sweep(new_sweep));
2427                }
2428            }
2429            return Ok(return_arr);
2430        }
2431        ModelingCmd::Solid3dGetAdjacencyInfo(kcmc::Solid3dGetAdjacencyInfo { .. }) => {
2432            let Some(OkModelingCmdResponse::Solid3dGetAdjacencyInfo(info)) = response else {
2433                return Ok(Vec::new());
2434            };
2435
2436            let mut return_arr = Vec::new();
2437            for (index, edge) in info.edges.iter().enumerate() {
2438                let Some(original_info) = &edge.original_info else {
2439                    continue;
2440                };
2441                let edge_id = ArtifactId::new(original_info.edge_id);
2442                let Some(artifact) = artifacts.get(&edge_id) else {
2443                    continue;
2444                };
2445                match artifact {
2446                    Artifact::Segment(segment) => {
2447                        let mut new_segment = segment.clone();
2448                        new_segment.common_surface_ids =
2449                            original_info.faces.iter().map(|face| ArtifactId::new(*face)).collect();
2450                        return_arr.push(Artifact::Segment(new_segment));
2451                    }
2452                    Artifact::SweepEdge(sweep_edge) => {
2453                        let mut new_sweep_edge = sweep_edge.clone();
2454                        new_sweep_edge.common_surface_ids =
2455                            original_info.faces.iter().map(|face| ArtifactId::new(*face)).collect();
2456                        return_arr.push(Artifact::SweepEdge(new_sweep_edge));
2457                    }
2458                    _ => {}
2459                };
2460
2461                let Some(Artifact::Segment(segment)) = artifacts.get(&edge_id) else {
2462                    continue;
2463                };
2464                let Some(surface_id) = segment.surface_id else {
2465                    continue;
2466                };
2467                let Some(Artifact::Wall(wall)) = artifacts.get(&surface_id) else {
2468                    continue;
2469                };
2470                let Some(Artifact::Sweep(sweep)) = artifacts.get(&wall.sweep_id) else {
2471                    continue;
2472                };
2473                let Some(Artifact::Path(_)) = artifacts.get(&sweep.path_id) else {
2474                    continue;
2475                };
2476
2477                if let Some(opposite_info) = &edge.opposite_info {
2478                    return_arr.push(Artifact::SweepEdge(SweepEdge {
2479                        id: opposite_info.edge_id.into(),
2480                        sub_type: SweepEdgeSubType::Opposite,
2481                        seg_id: edge_id,
2482                        cmd_id: artifact_command.cmd_id,
2483                        index,
2484                        sweep_id: sweep.id,
2485                        common_surface_ids: opposite_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(),
2486                    }));
2487                    let mut new_segment = segment.clone();
2488                    new_segment.edge_ids = vec![opposite_info.edge_id.into()];
2489                    return_arr.push(Artifact::Segment(new_segment));
2490                    let mut new_sweep = sweep.clone();
2491                    new_sweep.edge_ids = vec![opposite_info.edge_id.into()];
2492                    return_arr.push(Artifact::Sweep(new_sweep));
2493                    let mut new_wall = wall.clone();
2494                    new_wall.edge_cut_edge_ids = vec![opposite_info.edge_id.into()];
2495                    return_arr.push(Artifact::Wall(new_wall));
2496                }
2497                if let Some(adjacent_info) = &edge.adjacent_info {
2498                    return_arr.push(Artifact::SweepEdge(SweepEdge {
2499                        id: adjacent_info.edge_id.into(),
2500                        sub_type: SweepEdgeSubType::Adjacent,
2501                        seg_id: edge_id,
2502                        cmd_id: artifact_command.cmd_id,
2503                        index,
2504                        sweep_id: sweep.id,
2505                        common_surface_ids: adjacent_info.faces.iter().map(|face| ArtifactId::new(*face)).collect(),
2506                    }));
2507                    let mut new_segment = segment.clone();
2508                    new_segment.edge_ids = vec![adjacent_info.edge_id.into()];
2509                    return_arr.push(Artifact::Segment(new_segment));
2510                    let mut new_sweep = sweep.clone();
2511                    new_sweep.edge_ids = vec![adjacent_info.edge_id.into()];
2512                    return_arr.push(Artifact::Sweep(new_sweep));
2513                    let mut new_wall = wall.clone();
2514                    new_wall.edge_cut_edge_ids = vec![adjacent_info.edge_id.into()];
2515                    return_arr.push(Artifact::Wall(new_wall));
2516                }
2517            }
2518            return Ok(return_arr);
2519        }
2520        ModelingCmd::Solid3dMultiJoin(cmd) => {
2521            let mut return_arr = Vec::new();
2522            return_arr.push(Artifact::CompositeSolid(CompositeSolid {
2523                id,
2524                consumed: false,
2525                sub_type: CompositeSolidSubType::Union,
2526                output_index: None,
2527                solid_ids: cmd.object_ids.iter().map(|id| id.into()).collect(),
2528                tool_ids: vec![],
2529                code_ref,
2530                composite_solid_id: None,
2531                pattern_ids: Vec::new(),
2532            }));
2533
2534            let solid_ids = cmd.object_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
2535
2536            for input_id in &solid_ids {
2537                if let Some(artifact) = artifacts.get(input_id)
2538                    && let Artifact::CompositeSolid(comp) = artifact
2539                {
2540                    let mut new_comp = comp.clone();
2541                    new_comp.composite_solid_id = Some(id);
2542                    new_comp.consumed = true;
2543                    return_arr.push(Artifact::CompositeSolid(new_comp));
2544                } else if let Some(Artifact::Sweep(sweep)) = artifacts.get(input_id) {
2545                    let mut new_sweep = sweep.clone();
2546                    new_sweep.consumed = true;
2547                    return_arr.push(Artifact::Sweep(new_sweep));
2548                }
2549            }
2550            return Ok(return_arr);
2551        }
2552        ModelingCmd::Solid3dFilletEdge(cmd) => {
2553            let mut return_arr = Vec::new();
2554            let edge_id = if let Some(edge_id) = cmd.edge_id {
2555                ArtifactId::new(edge_id)
2556            } else {
2557                let Some(edge_id) = cmd.edge_ids.first() else {
2558                    internal_error!(
2559                        range,
2560                        "Solid3dFilletEdge command has no edge ID: id={id:?}, cmd={cmd:?}"
2561                    );
2562                };
2563                edge_id.into()
2564            };
2565            return_arr.push(Artifact::EdgeCut(EdgeCut {
2566                id,
2567                sub_type: cmd.cut_type.into(),
2568                consumed_edge_id: edge_id,
2569                edge_ids: Vec::new(),
2570                surface_id: None,
2571                code_ref,
2572            }));
2573            let consumed_edge = artifacts.get(&edge_id);
2574            if let Some(Artifact::Segment(consumed_edge)) = consumed_edge {
2575                let mut new_segment = consumed_edge.clone();
2576                new_segment.edge_cut_id = Some(id);
2577                return_arr.push(Artifact::Segment(new_segment));
2578            } else {
2579                // TODO: Handle other types like SweepEdge.
2580            }
2581            return Ok(return_arr);
2582        }
2583        ModelingCmd::Solid3dCutEdges(cmd) => {
2584            let mut return_arr = Vec::new();
2585            let edge_id = if let Some(edge_id) = cmd.edge_ids.first() {
2586                edge_id.into()
2587            } else {
2588                internal_error!(range, "Solid3dCutEdges command has no edge ID: id={id:?}, cmd={cmd:?}");
2589            };
2590            return_arr.push(Artifact::EdgeCut(EdgeCut {
2591                id,
2592                sub_type: cmd.cut_type.into(),
2593                consumed_edge_id: edge_id,
2594                edge_ids: Vec::new(),
2595                surface_id: None,
2596                code_ref,
2597            }));
2598            let consumed_edge = artifacts.get(&edge_id);
2599            if let Some(Artifact::Segment(consumed_edge)) = consumed_edge {
2600                let mut new_segment = consumed_edge.clone();
2601                new_segment.edge_cut_id = Some(id);
2602                return_arr.push(Artifact::Segment(new_segment));
2603            } else {
2604                // TODO: Handle other types like SweepEdge.
2605            }
2606            return Ok(return_arr);
2607        }
2608        ModelingCmd::EntityMakeHelix(cmd) => {
2609            let cylinder_id = ArtifactId::new(cmd.cylinder_id);
2610            let return_arr = vec![Artifact::Helix(Helix {
2611                id,
2612                axis_id: Some(cylinder_id),
2613                code_ref,
2614                trajectory_sweep_id: None,
2615                consumed: false,
2616            })];
2617            return Ok(return_arr);
2618        }
2619        ModelingCmd::EntityMakeHelixFromParams(_) => {
2620            let return_arr = vec![Artifact::Helix(Helix {
2621                id,
2622                axis_id: None,
2623                code_ref,
2624                trajectory_sweep_id: None,
2625                consumed: false,
2626            })];
2627            return Ok(return_arr);
2628        }
2629        ModelingCmd::EntityMakeHelixFromEdge(helix) => {
2630            let return_arr = vec![Artifact::Helix(Helix {
2631                id,
2632                axis_id: helix.edge_id.map(ArtifactId::new),
2633                code_ref,
2634                trajectory_sweep_id: None,
2635                consumed: false,
2636            })];
2637            // We could add the reverse graph edge connecting from the edge to
2638            // the helix here, but it's not useful right now.
2639            return Ok(return_arr);
2640        }
2641        ModelingCmd::Solid2dAddHole(solid2d_add_hole) => {
2642            let mut return_arr = Vec::new();
2643            // Add the hole to the outer.
2644            let outer_path = artifacts.get(&ArtifactId::new(solid2d_add_hole.object_id));
2645            if let Some(Artifact::Path(path)) = outer_path {
2646                let mut new_path = path.clone();
2647                new_path.inner_path_id = Some(ArtifactId::new(solid2d_add_hole.hole_id));
2648                return_arr.push(Artifact::Path(new_path));
2649            }
2650            // Add the outer to the hole.
2651            let inner_solid2d = artifacts.get(&ArtifactId::new(solid2d_add_hole.hole_id));
2652            if let Some(Artifact::Path(path)) = inner_solid2d {
2653                let mut new_path = path.clone();
2654                new_path.consumed = true;
2655                new_path.outer_path_id = Some(ArtifactId::new(solid2d_add_hole.object_id));
2656                return_arr.push(Artifact::Path(new_path));
2657            }
2658            return Ok(return_arr);
2659        }
2660        ModelingCmd::BooleanIntersection(_) | ModelingCmd::BooleanSubtract(_) | ModelingCmd::BooleanUnion(_) => {
2661            let (sub_type, solid_ids, tool_ids) = match cmd {
2662                ModelingCmd::BooleanIntersection(intersection) => {
2663                    let solid_ids = intersection
2664                        .solid_ids
2665                        .iter()
2666                        .copied()
2667                        .map(ArtifactId::new)
2668                        .collect::<Vec<_>>();
2669                    (CompositeSolidSubType::Intersect, solid_ids, Vec::new())
2670                }
2671                ModelingCmd::BooleanSubtract(subtract) => {
2672                    let solid_ids = subtract
2673                        .target_ids
2674                        .iter()
2675                        .copied()
2676                        .map(ArtifactId::new)
2677                        .collect::<Vec<_>>();
2678                    let tool_ids = subtract
2679                        .tool_ids
2680                        .iter()
2681                        .copied()
2682                        .map(ArtifactId::new)
2683                        .collect::<Vec<_>>();
2684                    (CompositeSolidSubType::Subtract, solid_ids, tool_ids)
2685                }
2686                ModelingCmd::BooleanUnion(union) => {
2687                    let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
2688                    (CompositeSolidSubType::Union, solid_ids, Vec::new())
2689                }
2690                _ => internal_error!(
2691                    range,
2692                    "Boolean or composite command variant not handled: id={id:?}, cmd={cmd:?}"
2693                ),
2694            };
2695
2696            let mut new_solid_ids = vec![id];
2697
2698            // Make sure we don't ever create a duplicate ID since merge_ids
2699            // can't handle it.
2700            let not_cmd_id = move |solid_id: &ArtifactId| *solid_id != id;
2701
2702            match (cmd, response) {
2703                (
2704                    ModelingCmd::BooleanSubtract(subtract_cmd),
2705                    Some(OkModelingCmdResponse::BooleanSubtract(subtract_resp)),
2706                ) => {
2707                    new_solid_ids = boolean_subtract_output_artifact_ids(
2708                        id,
2709                        &subtract_cmd.target_ids,
2710                        &subtract_cmd.tool_ids,
2711                        &subtract_resp.extra_solid_ids,
2712                    );
2713                }
2714                (_, Some(OkModelingCmdResponse::BooleanIntersection(intersection))) => intersection
2715                    .extra_solid_ids
2716                    .iter()
2717                    .copied()
2718                    .map(ArtifactId::new)
2719                    .filter(not_cmd_id)
2720                    .for_each(|id| new_solid_ids.push(id)),
2721                (_, Some(OkModelingCmdResponse::BooleanUnion(union))) => union
2722                    .extra_solid_ids
2723                    .iter()
2724                    .copied()
2725                    .map(ArtifactId::new)
2726                    .filter(not_cmd_id)
2727                    .for_each(|id| new_solid_ids.push(id)),
2728                _ => {}
2729            }
2730
2731            let mut return_arr = Vec::new();
2732            let mut consumed_sweep_ids = FnvHashSet::default();
2733            let mut input_ids = solid_ids.clone();
2734            merge_ids(&mut input_ids, tool_ids.clone());
2735
2736            if new_solid_ids.is_empty() {
2737                update_csg_input_artifacts(&mut return_arr, artifacts, &input_ids, None, &mut consumed_sweep_ids);
2738            }
2739
2740            // Create the new composite solids and update their linked artifacts
2741            for solid_id in &new_solid_ids {
2742                // Create the composite solid
2743                return_arr.push(Artifact::CompositeSolid(CompositeSolid {
2744                    id: *solid_id,
2745                    consumed: false,
2746                    sub_type,
2747                    output_index: None,
2748                    solid_ids: solid_ids.clone(),
2749                    tool_ids: tool_ids.clone(),
2750                    code_ref: code_ref.clone(),
2751                    composite_solid_id: None,
2752                    pattern_ids: Vec::new(),
2753                }));
2754
2755                update_csg_input_artifacts(
2756                    &mut return_arr,
2757                    artifacts,
2758                    &input_ids,
2759                    Some(*solid_id),
2760                    &mut consumed_sweep_ids,
2761                );
2762            }
2763
2764            return Ok(return_arr);
2765        }
2766        ModelingCmd::BooleanImprint(imprint) => {
2767            let solid_ids = imprint
2768                .body_ids
2769                .iter()
2770                .copied()
2771                .map(ArtifactId::new)
2772                .collect::<Vec<_>>();
2773            let tool_ids = imprint
2774                .tool_ids
2775                .as_ref()
2776                .map(|ids| ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>())
2777                .unwrap_or_default();
2778
2779            let mut new_solid_ids = vec![id];
2780            let not_cmd_id = move |solid_id: &ArtifactId| *solid_id != id;
2781            if let Some(OkModelingCmdResponse::BooleanImprint(imprint)) = response {
2782                imprint
2783                    .extra_solid_ids
2784                    .iter()
2785                    .copied()
2786                    .map(ArtifactId::new)
2787                    .filter(not_cmd_id)
2788                    .for_each(|id| new_solid_ids.push(id));
2789            }
2790
2791            let mut return_arr = Vec::new();
2792            let mut consumed_sweep_ids = FnvHashSet::default();
2793
2794            for input_id in solid_ids.iter().chain(tool_ids.iter()) {
2795                let sweep_id = match artifacts.get(input_id) {
2796                    Some(Artifact::Sweep(sweep)) => Some(sweep.id),
2797                    Some(Artifact::Path(path)) => path.sweep_id,
2798                    _ => None,
2799                };
2800
2801                if let Some(sweep_id) = sweep_id
2802                    && consumed_sweep_ids.insert(sweep_id)
2803                    && let Some(Artifact::Sweep(sweep)) = artifacts.get(&sweep_id)
2804                {
2805                    let mut new_sweep = sweep.clone();
2806                    new_sweep.consumed = true;
2807                    return_arr.push(Artifact::Sweep(new_sweep));
2808                }
2809            }
2810
2811            for (output_index, solid_id) in new_solid_ids.iter().enumerate() {
2812                return_arr.push(Artifact::CompositeSolid(CompositeSolid {
2813                    id: *solid_id,
2814                    consumed: false,
2815                    sub_type: CompositeSolidSubType::Split,
2816                    output_index: Some(output_index),
2817                    solid_ids: solid_ids.clone(),
2818                    tool_ids: tool_ids.clone(),
2819                    code_ref: code_ref.clone(),
2820                    composite_solid_id: None,
2821                    pattern_ids: Vec::new(),
2822                }));
2823
2824                for input_id in solid_ids.iter().chain(tool_ids.iter()) {
2825                    if let Some(artifact) = artifacts.get(input_id) {
2826                        match artifact {
2827                            Artifact::CompositeSolid(comp) => {
2828                                let mut new_comp = comp.clone();
2829                                new_comp.composite_solid_id = Some(*solid_id);
2830                                new_comp.consumed = true;
2831                                return_arr.push(Artifact::CompositeSolid(new_comp));
2832                            }
2833                            Artifact::Path(path) => {
2834                                let mut new_path = path.clone();
2835                                new_path.composite_solid_id = Some(*solid_id);
2836
2837                                return_arr.push(Artifact::Path(new_path));
2838                            }
2839                            _ => {}
2840                        }
2841                    }
2842                }
2843            }
2844
2845            return Ok(return_arr);
2846        }
2847        _ => {}
2848    }
2849
2850    Ok(Vec::new())
2851}