Fix artifact graph to survive when there's an execution error (#5246)

This also changes artifact graph errors to point to the KCL that the
command originated from.
This commit is contained in:
Jonathan Tran
2025-02-04 20:34:56 -05:00
committed by GitHub
parent 30ac0e4f48
commit 6ce270c0d0
2 changed files with 51 additions and 25 deletions

View File

@ -158,6 +158,12 @@ export function isTopLevelModule(range: SourceRange): boolean {
return range[2] === 0 return range[2] === 0
} }
function firstSourceRange(error: RustKclError): SourceRange {
return error.sourceRanges.length > 0
? sourceRangeFromRust(error.sourceRanges[0])
: defaultSourceRange()
}
export const wasmUrl = () => { export const wasmUrl = () => {
// For when we're in electron (file based) or web server (network based) // For when we're in electron (file based) or web server (network based)
// For some reason relative paths don't work as expected. Otherwise we would // For some reason relative paths don't work as expected. Otherwise we would
@ -255,7 +261,7 @@ export const parse = (code: string | Error): ParseResult | Error => {
return new KCLError( return new KCLError(
parsed.kind, parsed.kind,
parsed.msg, parsed.msg,
sourceRangeFromRust(parsed.sourceRanges[0]), firstSourceRange(parsed),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph()
@ -622,7 +628,7 @@ export const executor = async (
const kclError = new KCLError( const kclError = new KCLError(
parsed.error.kind, parsed.error.kind,
parsed.error.msg, parsed.error.msg,
sourceRangeFromRust(parsed.error.sourceRanges[0]), firstSourceRange(parsed.error),
parsed.operations, parsed.operations,
parsed.artifactCommands, parsed.artifactCommands,
rustArtifactGraphToMap(parsed.artifactGraph) rustArtifactGraphToMap(parsed.artifactGraph)
@ -691,7 +697,7 @@ export const modifyAstForSketch = async (
const kclError = new KCLError( const kclError = new KCLError(
parsed.kind, parsed.kind,
parsed.msg, parsed.msg,
sourceRangeFromRust(parsed.sourceRanges[0]), firstSourceRange(parsed),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph()
@ -762,7 +768,7 @@ export function programMemoryInit(): ProgramMemory | Error {
return new KCLError( return new KCLError(
parsed.kind, parsed.kind,
parsed.msg, parsed.msg,
sourceRangeFromRust(parsed.sourceRanges[0]), firstSourceRange(parsed),
[], [],
[], [],
defaultArtifactGraph() defaultArtifactGraph()

View File

@ -12,6 +12,7 @@ use serde::{ser::SerializeSeq, Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
errors::KclErrorDetails,
parsing::ast::types::{Node, Program}, parsing::ast::types::{Node, Program},
KclError, SourceRange, KclError, SourceRange,
}; };
@ -595,9 +596,12 @@ fn artifacts_to_update(
} }
ModelingCmd::EnableSketchMode(_) => { ModelingCmd::EnableSketchMode(_) => {
let current_plane_id = current_plane_id.ok_or_else(|| { let current_plane_id = current_plane_id.ok_or_else(|| {
KclError::internal(format!( KclError::Internal(KclErrorDetails {
"Expected a current plane ID when processing EnableSketchMode command, but we have none: {id:?}" message: format!(
)) "Expected a current plane ID when processing EnableSketchMode command, but we have none: {id:?}"
),
source_ranges: vec![range],
})
})?; })?;
let existing_plane = artifacts.get(&ArtifactId::new(current_plane_id)); let existing_plane = artifacts.get(&ArtifactId::new(current_plane_id));
match existing_plane { match existing_plane {
@ -626,9 +630,12 @@ fn artifacts_to_update(
ModelingCmd::StartPath(_) => { ModelingCmd::StartPath(_) => {
let mut return_arr = Vec::new(); let mut return_arr = Vec::new();
let current_plane_id = current_plane_id.ok_or_else(|| { let current_plane_id = current_plane_id.ok_or_else(|| {
KclError::internal(format!( KclError::Internal(KclErrorDetails {
"Expected a current plane ID when processing StartPath command, but we have none: {id:?}" message: format!(
)) "Expected a current plane ID when processing StartPath command, but we have none: {id:?}"
),
source_ranges: vec![range],
})
})?; })?;
return_arr.push(Artifact::Path(Path { return_arr.push(Artifact::Path(Path {
id, id,
@ -730,9 +737,10 @@ fn artifacts_to_update(
// TODO: Using the first one. Make sure to revisit this // TODO: Using the first one. Make sure to revisit this
// choice, don't think it matters for now. // choice, don't think it matters for now.
path_id: ArtifactId::new(*loft_cmd.section_ids.first().ok_or_else(|| { path_id: ArtifactId::new(*loft_cmd.section_ids.first().ok_or_else(|| {
KclError::internal(format!( KclError::Internal(KclErrorDetails {
"Expected at least one section ID in Loft command: {id:?}; cmd={cmd:?}" message: format!("Expected at least one section ID in Loft command: {id:?}; cmd={cmd:?}"),
)) source_ranges: vec![range],
})
})?), })?),
surface_ids: Vec::new(), surface_ids: Vec::new(),
edge_ids: Vec::new(), edge_ids: Vec::new(),
@ -772,9 +780,12 @@ fn artifacts_to_update(
}; };
last_path = Some(path); last_path = Some(path);
let path_sweep_id = path.sweep_id.ok_or_else(|| { let path_sweep_id = path.sweep_id.ok_or_else(|| {
KclError::internal(format!( KclError::Internal(KclErrorDetails {
"Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}" message:format!(
)) "Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
),
source_ranges: vec![range],
})
})?; })?;
return_arr.push(Artifact::Wall(Wall { return_arr.push(Artifact::Wall(Wall {
id: face_id, id: face_id,
@ -803,9 +814,12 @@ fn artifacts_to_update(
continue; continue;
}; };
let path_sweep_id = path.sweep_id.ok_or_else(|| { let path_sweep_id = path.sweep_id.ok_or_else(|| {
KclError::internal(format!( KclError::Internal(KclErrorDetails {
"Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}" message:format!(
)) "Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
),
source_ranges: vec![range],
})
})?; })?;
return_arr.push(Artifact::Cap(Cap { return_arr.push(Artifact::Cap(Cap {
id: face_id, id: face_id,
@ -848,17 +862,23 @@ fn artifacts_to_update(
let response_edge_id = match response { let response_edge_id = match response {
OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(r) => { OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(r) => {
let Some(edge_id) = r.edge else { let Some(edge_id) = r.edge else {
return Err(KclError::internal(format!( return Err(KclError::Internal(KclErrorDetails {
"Expected Solid3dGetNextAdjacentEdge response to have an edge ID, but found none: id={id:?}, {response:?}" message:format!(
))); "Expected Solid3dGetNextAdjacentEdge response to have an edge ID, but found none: id={id:?}, {response:?}"
),
source_ranges: vec![range],
}));
}; };
edge_id.into() edge_id.into()
} }
OkModelingCmdResponse::Solid3dGetOppositeEdge(r) => r.edge.into(), OkModelingCmdResponse::Solid3dGetOppositeEdge(r) => r.edge.into(),
_ => { _ => {
return Err(KclError::internal(format!( return Err(KclError::Internal(KclErrorDetails {
"Expected Solid3dGetNextAdjacentEdge or Solid3dGetOppositeEdge response, but got: id={id:?}, {response:?}" message:format!(
))); "Expected Solid3dGetNextAdjacentEdge or Solid3dGetOppositeEdge response, but got: id={id:?}, {response:?}"
),
source_ranges: vec![range],
}));
} }
}; };