diff --git a/rust/kcl-lib/src/execution/artifact.rs b/rust/kcl-lib/src/execution/artifact.rs index 251d1fc996..f5505acde3 100644 --- a/rust/kcl-lib/src/execution/artifact.rs +++ b/rust/kcl-lib/src/execution/artifact.rs @@ -85,10 +85,8 @@ pub struct CompositeSolid { pub id: ArtifactId, pub sub_type: CompositeSolidSubType, /// Constituent solids of the composite solid. - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub solid_ids: Vec, /// Tool solids used for asymmetric operations like subtract. - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub tool_ids: Vec, pub code_ref: CodeRef, /// This is the ID of the composite solid that this is part of, if any, as a @@ -141,12 +139,10 @@ pub struct Segment { pub path_id: ArtifactId, #[serde(default, skip_serializing_if = "Option::is_none")] pub surface_id: Option, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub edge_ids: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub edge_cut_id: Option, pub code_ref: CodeRef, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub common_surface_ids: Vec, } @@ -158,9 +154,7 @@ pub struct Sweep { pub id: ArtifactId, pub sub_type: SweepSubType, pub path_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub surface_ids: Vec, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub edge_ids: Vec, pub code_ref: CodeRef, } @@ -209,10 +203,8 @@ pub struct StartSketchOnPlane { pub struct Wall { pub id: ArtifactId, pub seg_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub edge_cut_edge_ids: Vec, pub sweep_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub path_ids: Vec, /// This is for the sketch-on-face plane, not for the wall itself. Traverse /// to the extrude and/or segment to get the wall's code_ref. @@ -227,10 +219,8 @@ pub struct Wall { pub struct Cap { pub id: ArtifactId, pub sub_type: CapSubType, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub edge_cut_edge_ids: Vec, pub sweep_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub path_ids: Vec, /// This is for the sketch-on-face plane, not for the cap itself. Traverse /// to the extrude and/or segment to get the cap's code_ref. @@ -259,7 +249,6 @@ pub struct SweepEdge { #[serde(skip)] pub index: usize, pub sweep_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub common_surface_ids: Vec, } @@ -278,7 +267,6 @@ pub struct EdgeCut { pub id: ArtifactId, pub sub_type: EdgeCutSubType, pub consumed_edge_id: ArtifactId, - #[serde(default, skip_serializing_if = "Vec::is_empty")] pub edge_ids: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub surface_id: Option, @@ -540,6 +528,11 @@ impl ArtifactGraph { self.map.is_empty() } + #[cfg(test)] + pub(crate) fn iter(&self) -> impl Iterator { + self.map.iter() + } + pub fn values(&self) -> impl Iterator { self.map.values() } @@ -728,10 +721,7 @@ fn artifacts_to_update( exec_artifacts: &IndexMap, ) -> Result, KclError> { let uuid = artifact_command.cmd_id; - let Some(response) = responses.get(&uuid) else { - // Response not found or not successful. - return Ok(Vec::new()); - }; + let response = responses.get(&uuid); // TODO: Build path-to-node from artifact_command source range. Right now, // we're serializing an empty array, and the TS wrapper fills it in with the @@ -875,7 +865,7 @@ fn artifacts_to_update( new_path.seg_ids = vec![id]; return_arr.push(Artifact::Path(new_path)); } - if let OkModelingCmdResponse::ClosePath(close_path) = response { + if let Some(OkModelingCmdResponse::ClosePath(close_path)) = response { return_arr.push(Artifact::Solid2d(Solid2d { id: close_path.face_id.into(), path_id, @@ -895,8 +885,8 @@ fn artifacts_to_update( ids: original_path_ids, .. }) => { let face_edge_infos = match response { - OkModelingCmdResponse::EntityMirror(resp) => &resp.entity_face_edge_ids, - OkModelingCmdResponse::EntityMirrorAcrossEdge(resp) => &resp.entity_face_edge_ids, + Some(OkModelingCmdResponse::EntityMirror(resp)) => &resp.entity_face_edge_ids, + Some(OkModelingCmdResponse::EntityMirrorAcrossEdge(resp)) => &resp.entity_face_edge_ids, _ => internal_error!( range, "Mirror response variant not handled: id={id:?}, cmd={cmd:?}, response={response:?}" @@ -983,7 +973,7 @@ fn artifacts_to_update( return Ok(return_arr); } ModelingCmd::Loft(loft_cmd) => { - let OkModelingCmdResponse::Loft(_) = response else { + let Some(OkModelingCmdResponse::Loft(_)) = response else { return Ok(Vec::new()); }; let mut return_arr = Vec::new(); @@ -1013,7 +1003,7 @@ fn artifacts_to_update( return Ok(return_arr); } ModelingCmd::Solid3dGetExtrusionFaceInfo(_) => { - let OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(face_info) = response else { + let Some(OkModelingCmdResponse::Solid3dGetExtrusionFaceInfo(face_info)) = response else { return Ok(Vec::new()); }; let mut return_arr = Vec::new(); @@ -1134,7 +1124,7 @@ fn artifacts_to_update( return Ok(return_arr); } ModelingCmd::Solid3dGetAdjacencyInfo(kcmc::Solid3dGetAdjacencyInfo { .. }) => { - let OkModelingCmdResponse::Solid3dGetAdjacencyInfo(info) = response else { + let Some(OkModelingCmdResponse::Solid3dGetAdjacencyInfo(info)) = response else { return Ok(Vec::new()); }; @@ -1315,21 +1305,21 @@ fn artifacts_to_update( let not_cmd_id = move |solid_id: &ArtifactId| *solid_id != id; match response { - OkModelingCmdResponse::BooleanIntersection(intersection) => intersection + Some(OkModelingCmdResponse::BooleanIntersection(intersection)) => intersection .extra_solid_ids .iter() .copied() .map(ArtifactId::new) .filter(not_cmd_id) .for_each(|id| new_solid_ids.push(id)), - OkModelingCmdResponse::BooleanSubtract(subtract) => subtract + Some(OkModelingCmdResponse::BooleanSubtract(subtract)) => subtract .extra_solid_ids .iter() .copied() .map(ArtifactId::new) .filter(not_cmd_id) .for_each(|id| new_solid_ids.push(id)), - OkModelingCmdResponse::BooleanUnion(union) => union + Some(OkModelingCmdResponse::BooleanUnion(union)) => union .extra_solid_ids .iter() .copied() diff --git a/rust/kcl-lib/src/execution/cache.rs b/rust/kcl-lib/src/execution/cache.rs index 308e39aa46..3382e746ed 100644 --- a/rust/kcl-lib/src/execution/cache.rs +++ b/rust/kcl-lib/src/execution/cache.rs @@ -20,7 +20,7 @@ use crate::{ lazy_static::lazy_static! { /// A static mutable lock for updating the last successful execution state for the cache. static ref OLD_AST: Arc>> = Default::default(); - // The last successful run's memory. Not cleared after an unssuccessful run. + // The last successful run's memory. Not cleared after an unsuccessful run. static ref PREV_MEMORY: Arc>> = Default::default(); } diff --git a/rust/kcl-lib/src/execution/mod.rs b/rust/kcl-lib/src/execution/mod.rs index a4b8ab7494..9c69ac2d61 100644 --- a/rust/kcl-lib/src/execution/mod.rs +++ b/rust/kcl-lib/src/execution/mod.rs @@ -2286,6 +2286,25 @@ w = f() + f() ctx2.close().await; } + #[cfg(feature = "artifact-graph")] + #[tokio::test(flavor = "multi_thread")] + async fn mock_has_stable_ids() { + let ctx = ExecutorContext::new_mock(None).await; + let code = "sk = startSketchOn(XY) + |> startProfile(at = [0, 0])"; + let program = crate::Program::parse_no_errs(code).unwrap(); + let result = ctx.run_mock(program, false).await.unwrap(); + let ids = result.artifact_graph.iter().map(|(k, _)| *k).collect::>(); + assert!(!ids.is_empty(), "IDs should not be empty"); + + let ctx2 = ExecutorContext::new_mock(None).await; + let program2 = crate::Program::parse_no_errs(code).unwrap(); + let result = ctx2.run_mock(program2, false).await.unwrap(); + let ids2 = result.artifact_graph.iter().map(|(k, _)| *k).collect::>(); + + assert_eq!(ids, ids2, "Generated IDs should match"); + } + #[cfg(feature = "artifact-graph")] #[tokio::test(flavor = "multi_thread")] async fn sim_sketch_mode_real_mock_real() { diff --git a/rust/kcl-lib/src/execution/state.rs b/rust/kcl-lib/src/execution/state.rs index e41f370b31..53614c6440 100644 --- a/rust/kcl-lib/src/execution/state.rs +++ b/rust/kcl-lib/src/execution/state.rs @@ -177,7 +177,7 @@ impl ExecState { #[cfg(feature = "artifact-graph")] operations: Default::default(), #[cfg(feature = "artifact-graph")] - artifact_graph: Default::default(), + artifact_graph: self.global.artifacts.graph, errors: self.global.errors, filenames: Default::default(), default_planes: ctx.engine.get_default_planes().read().await.clone(), diff --git a/rust/kcl-lib/tests/execute_engine_error_return/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/execute_engine_error_return/artifact_graph_flowchart.snap.md index 14c32b6c85..cb7db69520 100644 --- a/rust/kcl-lib/tests/execute_engine_error_return/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/execute_engine_error_return/artifact_graph_flowchart.snap.md @@ -14,9 +14,12 @@ flowchart LR end 1["Plane
[10, 27, 0]"] %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] + 7["Sweep Extrusion
[226, 245, 0]"] + %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }] 1 --- 2 2 --- 3 2 --- 4 2 --- 5 2 --- 6 + 2 ---- 7 ``` diff --git a/rust/kcl-lib/tests/multi_target_csg/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/multi_target_csg/artifact_graph_flowchart.snap.md index 88e52f65e3..118d7a835b 100644 --- a/rust/kcl-lib/tests/multi_target_csg/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/multi_target_csg/artifact_graph_flowchart.snap.md @@ -185,7 +185,9 @@ flowchart LR %% face_code_ref=Missing NodePath 110["SweepEdge Opposite"] 111["SweepEdge Adjacent"] - 112["StartSketchOnPlane
[1430, 1475, 0]"] + 112["CompositeSolid Subtract
[1634, 1677, 0]"] + %% [ProgramBodyItem { index: 10 }, VariableDeclarationDeclaration, VariableDeclarationInit] + 113["StartSketchOnPlane
[1430, 1475, 0]"] %% [ProgramBodyItem { index: 7 }, VariableDeclarationDeclaration, VariableDeclarationInit] 1 --- 2 1 --- 13 @@ -302,6 +304,7 @@ flowchart LR 44 --- 50 44 --- 51 44 ---- 60 + 44 --- 112 45 --- 61 45 x--> 67 45 --- 69 @@ -334,6 +337,7 @@ flowchart LR 52 --- 58 52 --- 59 52 ---- 81 + 52 --- 112 53 --- 87 53 x--> 88 53 --- 100 @@ -447,10 +451,11 @@ flowchart LR 98 <--x 89 100 <--x 89 102 --- 103 - 102 <--x 112 + 102 <--x 113 103 --- 104 103 --- 105 103 ---- 106 + 103 --- 112 104 --- 107 104 x--> 109 104 --- 110 diff --git a/rust/kcl-lib/tests/pattern_into_union/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/pattern_into_union/artifact_graph_flowchart.snap.md index 4b7b0b2595..706ffb9447 100644 --- a/rust/kcl-lib/tests/pattern_into_union/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/pattern_into_union/artifact_graph_flowchart.snap.md @@ -139,6 +139,8 @@ flowchart LR 83["SweepEdge Adjacent"] 84["EdgeCut Fillet
[1691, 1773, 0]"] %% [ProgramBodyItem { index: 14 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }] + 85["CompositeSolid Union
[1939, 1960, 0]"] + %% [ProgramBodyItem { index: 15 }, ExpressionStatementExpr] 1 --- 2 2 --- 3 2 --- 4 @@ -150,6 +152,7 @@ flowchart LR 2 --- 10 2 --- 11 2 ---- 12 + 2 --- 85 3 --- 13 3 x--> 22 3 --- 23 @@ -247,6 +250,7 @@ flowchart LR 40 --- 44 40 --- 45 40 ---- 46 + 40 --- 85 41 --- 47 41 x--> 52 41 --- 53 diff --git a/rust/kcl-lib/tests/user_reported_union_2_bug/artifact_graph_flowchart.snap.md b/rust/kcl-lib/tests/user_reported_union_2_bug/artifact_graph_flowchart.snap.md index 4617f750e3..a12159211a 100644 --- a/rust/kcl-lib/tests/user_reported_union_2_bug/artifact_graph_flowchart.snap.md +++ b/rust/kcl-lib/tests/user_reported_union_2_bug/artifact_graph_flowchart.snap.md @@ -68,6 +68,8 @@ flowchart LR 38["SweepEdge Adjacent"] 39["SweepEdge Opposite"] 40["SweepEdge Adjacent"] + 41["CompositeSolid Union
[1346, 1377, 0]"] + %% [ProgramBodyItem { index: 11 }, VariableDeclarationDeclaration, VariableDeclarationInit] 1 --- 2 2 --- 3 2 --- 4 @@ -75,6 +77,7 @@ flowchart LR 2 --- 6 2 --- 7 2 ---- 8 + 2 --- 41 3 --- 9 3 x--> 13 3 --- 15 @@ -127,6 +130,7 @@ flowchart LR 24 --- 27 24 --- 28 24 ---- 29 + 24 --- 41 25 --- 30 25 x--> 33 25 --- 35 diff --git a/src/lang/KclSingleton.ts b/src/lang/KclSingleton.ts index f85cff26d6..588d5f6e5f 100644 --- a/src/lang/KclSingleton.ts +++ b/src/lang/KclSingleton.ts @@ -572,6 +572,7 @@ export class KclManager extends EventTarget { this.lastSuccessfulVariables = execState.variables this.lastSuccessfulOperations = execState.operations } + await this.updateArtifactGraph(execState.artifactGraph) return null } cancelAllExecutions() { diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index 2b77bc04e5..9164da2f2e 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -314,14 +314,7 @@ export function emptyExecState(): ExecState { } export function execStateFromRust(execOutcome: RustExecOutcome): ExecState { - const artifactGraph = rustArtifactGraphToMap(execOutcome.artifactGraph) - // Translate NodePath to PathToNode. - for (const [_id, artifact] of artifactGraph) { - if (!artifact) continue - if (!('codeRef' in artifact)) continue - const pathToNode = pathToNodeFromRustNodePath(artifact.codeRef.nodePath) - artifact.codeRef.pathToNode = pathToNode - } + const artifactGraph = artifactGraphFromRust(execOutcome.artifactGraph) return { variables: execOutcome.variables, @@ -333,29 +326,26 @@ export function execStateFromRust(execOutcome: RustExecOutcome): ExecState { } } -export function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState { - return { - variables: execOutcome.variables, - operations: execOutcome.operations, - artifactGraph: new Map(), - errors: execOutcome.errors, - filenames: execOutcome.filenames, - defaultPlanes: execOutcome.defaultPlanes, - } -} - export type ArtifactGraph = Map -function rustArtifactGraphToMap( +function artifactGraphFromRust( rustArtifactGraph: RustArtifactGraph ): ArtifactGraph { - const map = new Map() + const artifactGraph = new Map() + // Convert to a Map. for (const [id, artifact] of Object.entries(rustArtifactGraph.map)) { if (!artifact) continue - map.set(id, artifact) + artifactGraph.set(id, artifact) } - return map + // Translate NodePath to PathToNode. + for (const [_id, artifact] of artifactGraph) { + if (!artifact) continue + if (!('codeRef' in artifact)) continue + const pathToNode = pathToNodeFromRustNodePath(artifact.codeRef.nodePath) + artifact.codeRef.pathToNode = pathToNode + } + return artifactGraph } export function sketchFromKclValueOptional( @@ -407,7 +397,7 @@ export const errFromErrWithOutputs = (e: any): KCLError => { parsed.error.details.backtrace, parsed.nonFatal, parsed.operations, - rustArtifactGraphToMap(parsed.artifactGraph), + artifactGraphFromRust(parsed.artifactGraph), parsed.filenames, parsed.defaultPlanes ) diff --git a/src/lib/rustContext.ts b/src/lib/rustContext.ts index ab626cdc8f..a09736607d 100644 --- a/src/lib/rustContext.ts +++ b/src/lib/rustContext.ts @@ -13,11 +13,7 @@ import type { Models } from '@kittycad/lib/dist/types/src' import type { EngineCommandManager } from '@src/lang/std/engineConnection' import { fileSystemManager } from '@src/lang/std/fileSystemManager' import type { ExecState } from '@src/lang/wasm' -import { - errFromErrWithOutputs, - execStateFromRust, - mockExecStateFromRust, -} from '@src/lang/wasm' +import { errFromErrWithOutputs, execStateFromRust } from '@src/lang/wasm' import { initPromise } from '@src/lang/wasmUtils' import type ModelingAppFile from '@src/lib/modelingAppFile' import type { DefaultPlaneStr } from '@src/lib/planes' @@ -120,7 +116,7 @@ export default class RustContext { JSON.stringify(settings), usePrevMemory ) - return mockExecStateFromRust(result) + return execStateFromRust(result) } catch (e: any) { return Promise.reject(errFromErrWithOutputs(e)) }