1use indexmap::IndexMap;
2use kcl_error::ModuleId;
3use kcl_error::SourceRange;
4use parse_display::Display;
5use parse_display::FromStr;
6use schemars::JsonSchema;
7use serde::Deserialize;
8use serde::Serialize;
9use serde::ser::SerializeSeq;
10use uuid::Uuid;
11
12use crate::ArtifactId;
13use crate::NodePath;
14use crate::ObjectId;
15use crate::UnitLength;
16
17pub type DummyPathToNode = Vec<()>;
18
19fn serialize_dummy_path_to_node<S>(_path_to_node: &DummyPathToNode, serializer: S) -> Result<S::Ok, S::Error>
20where
21 S: serde::Serializer,
22{
23 let seq = serializer.serialize_seq(Some(0))?;
24 seq.end()
25}
26
27#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
28#[ts(export_to = "Artifact.ts")]
29#[serde(rename_all = "camelCase")]
30pub struct CodeRef {
31 pub range: SourceRange,
32 pub node_path: NodePath,
33 #[serde(default, serialize_with = "serialize_dummy_path_to_node")]
35 #[ts(type = "Array<[string | number, string]>")]
36 pub path_to_node: DummyPathToNode,
37}
38
39impl CodeRef {
40 pub fn placeholder(range: SourceRange) -> Self {
41 Self {
42 range,
43 node_path: Default::default(),
44 path_to_node: Vec::new(),
45 }
46 }
47}
48
49#[derive(Debug, Hash, Eq, Copy, Clone, Deserialize, Serialize, JsonSchema, PartialEq, ts_rs::TS, Display, FromStr)]
50#[ts(export)]
51#[serde(rename_all = "camelCase")]
52pub enum PlaneName {
53 #[display("XY")]
55 Xy,
56 #[display("-XY")]
58 NegXy,
59 #[display("XZ")]
61 Xz,
62 #[display("-XZ")]
64 NegXz,
65 #[display("YZ")]
67 Yz,
68 #[display("-YZ")]
70 NegYz,
71}
72
73#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Default, ts_rs::TS)]
76#[ts(export_to = "Artifact.ts")]
77pub struct ArtifactPoint3d {
78 pub x: f64,
79 pub y: f64,
80 pub z: f64,
81 pub units: Option<UnitLength>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
87#[ts(export_to = "Artifact.ts")]
88#[serde(rename_all = "camelCase")]
89pub struct ArtifactPlaneInfo {
90 pub origin: ArtifactPoint3d,
91 pub x_axis: ArtifactPoint3d,
92 pub y_axis: ArtifactPoint3d,
93 pub z_axis: ArtifactPoint3d,
94}
95
96#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
99#[ts(export_to = "Artifact.ts")]
100#[serde(rename_all = "snake_case")]
101pub enum ArtifactSweepMethod {
102 New,
103 Merge,
104}
105
106#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
110#[ts(export_to = "Artifact.ts")]
111#[serde(rename_all = "camelCase")]
112pub enum ArtifactOrientation {
113 Front,
114 Back,
115 Left,
116 Right,
117 Top,
118 Bottom,
119 Isometric,
120}
121
122#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
125#[ts(export_to = "Artifact.ts")]
126#[serde(rename_all = "camelCase")]
127pub enum ArtifactProjection {
128 Orthographic,
129 Perspective,
130}
131
132#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
136#[ts(export_to = "Artifact.ts")]
137#[serde(rename_all = "camelCase")]
138pub enum ArtifactVisibility {
139 Show,
140 Hide,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
147#[ts(export_to = "Artifact.ts")]
148#[serde(tag = "type", rename_all = "camelCase")]
149pub enum ArtifactCameraLook {
150 Oriented { orientation: ArtifactOrientation },
152 Directed {
156 direction: ArtifactPoint3d,
157 up: ArtifactPoint3d,
158 },
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
169#[ts(export_to = "Artifact.ts")]
170#[serde(rename_all = "camelCase")]
171pub struct ArtifactCameraView {
172 pub look: ArtifactCameraLook,
173 pub target: Option<ArtifactPoint3d>,
176 pub distance: Option<f64>,
179 pub projection: ArtifactProjection,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
183#[ts(export_to = "Artifact.ts")]
184#[serde(rename_all = "camelCase")]
185pub struct CompositeSolid {
186 pub id: ArtifactId,
187 pub consumed: bool,
189 pub sub_type: CompositeSolidSubType,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub output_index: Option<usize>,
194 pub solid_ids: Vec<ArtifactId>,
196 pub tool_ids: Vec<ArtifactId>,
198 pub code_ref: CodeRef,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub composite_solid_id: Option<ArtifactId>,
203 #[serde(default, skip_serializing_if = "Vec::is_empty")]
205 pub pattern_ids: Vec<ArtifactId>,
206}
207
208#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
209#[ts(export_to = "Artifact.ts")]
210#[serde(rename_all = "camelCase")]
211pub enum CompositeSolidSubType {
212 Intersect,
213 Subtract,
214 Split,
215 Union,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
219#[ts(export_to = "Artifact.ts")]
220#[serde(rename_all = "camelCase")]
221pub struct Plane {
222 pub id: ArtifactId,
223 pub path_ids: Vec<ArtifactId>,
224 pub code_ref: CodeRef,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
228#[ts(export_to = "Artifact.ts")]
229#[serde(rename_all = "camelCase")]
230pub struct Path {
231 pub id: ArtifactId,
232 pub sub_type: PathSubType,
233 pub plane_id: ArtifactId,
234 pub seg_ids: Vec<ArtifactId>,
235 pub consumed: bool,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
238 pub sweep_id: Option<ArtifactId>,
241 pub trajectory_sweep_id: Option<ArtifactId>,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
244 pub solid2d_id: Option<ArtifactId>,
245 pub code_ref: CodeRef,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
249 pub composite_solid_id: Option<ArtifactId>,
250 #[serde(default, skip_serializing_if = "Option::is_none")]
253 pub sketch_block_id: Option<ArtifactId>,
254 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub origin_path_id: Option<ArtifactId>,
258 #[serde(default, skip_serializing_if = "Option::is_none")]
260 pub inner_path_id: Option<ArtifactId>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub outer_path_id: Option<ArtifactId>,
265 #[serde(default, skip_serializing_if = "Vec::is_empty")]
267 pub pattern_ids: Vec<ArtifactId>,
268}
269
270#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
271#[ts(export_to = "Artifact.ts")]
272#[serde(rename_all = "camelCase")]
273pub enum PathSubType {
274 Sketch,
275 Region,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
279#[ts(export_to = "Artifact.ts")]
280#[serde(rename_all = "camelCase")]
281pub struct Segment {
282 pub id: ArtifactId,
283 pub path_id: ArtifactId,
284 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub source_segment_id: Option<ArtifactId>,
288 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub original_seg_id: Option<ArtifactId>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub surface_id: Option<ArtifactId>,
294 pub edge_ids: Vec<ArtifactId>,
295 #[serde(default, skip_serializing_if = "Option::is_none")]
296 pub edge_cut_id: Option<ArtifactId>,
297 pub code_ref: CodeRef,
298 pub common_surface_ids: Vec<ArtifactId>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
303#[ts(export_to = "Artifact.ts")]
304#[serde(rename_all = "camelCase")]
305pub struct Sweep {
306 pub id: ArtifactId,
307 pub sub_type: SweepSubType,
308 #[serde(default, skip_serializing_if = "Option::is_none")]
309 pub path_id: Option<ArtifactId>,
310 pub surface_ids: Vec<ArtifactId>,
311 pub edge_ids: Vec<ArtifactId>,
312 pub code_ref: CodeRef,
313 #[serde(default, skip_serializing_if = "Option::is_none")]
316 pub source_sweep_id: Option<ArtifactId>,
317 pub trajectory_id: Option<ArtifactId>,
321 pub method: ArtifactSweepMethod,
322 pub consumed: bool,
324 #[serde(default, skip_serializing_if = "Vec::is_empty")]
326 pub pattern_ids: Vec<ArtifactId>,
327}
328
329#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
330#[ts(export_to = "Artifact.ts")]
331#[serde(rename_all = "camelCase")]
332pub enum SweepSubType {
333 Extrusion,
334 ExtrusionTwist,
335 Revolve,
336 RevolveAboutEdge,
337 Loft,
338 Blend,
339 Sweep,
340}
341
342#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
343#[ts(export_to = "Artifact.ts")]
344#[serde(rename_all = "camelCase")]
345pub struct Solid2d {
346 pub id: ArtifactId,
347 pub path_id: ArtifactId,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
351#[ts(export_to = "Artifact.ts")]
352#[serde(rename_all = "camelCase")]
353pub struct PrimitiveFace {
354 pub id: ArtifactId,
355 pub solid_id: ArtifactId,
356 pub code_ref: CodeRef,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
360#[ts(export_to = "Artifact.ts")]
361#[serde(rename_all = "camelCase")]
362pub struct PrimitiveEdge {
363 pub id: ArtifactId,
364 pub solid_id: ArtifactId,
365 pub code_ref: CodeRef,
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
369#[ts(export_to = "Artifact.ts")]
370#[serde(rename_all = "camelCase")]
371pub struct PlaneOfFace {
372 pub id: ArtifactId,
373 pub face_id: ArtifactId,
374 pub code_ref: CodeRef,
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
378#[ts(export_to = "Artifact.ts")]
379#[serde(rename_all = "camelCase")]
380pub struct StartSketchOnFace {
381 pub id: ArtifactId,
382 pub face_id: ArtifactId,
383 pub code_ref: CodeRef,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
387#[ts(export_to = "Artifact.ts")]
388#[serde(rename_all = "camelCase")]
389pub struct StartSketchOnPlane {
390 pub id: ArtifactId,
391 pub plane_id: ArtifactId,
392 pub code_ref: CodeRef,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
396#[ts(export_to = "Artifact.ts")]
397#[serde(rename_all = "camelCase")]
398pub struct SketchBlock {
399 pub id: ArtifactId,
400 #[serde(default, skip_serializing_if = "Option::is_none")]
402 pub standard_plane: Option<PlaneName>,
403 #[serde(default, skip_serializing_if = "Option::is_none")]
405 pub plane_id: Option<ArtifactId>,
406 #[serde(default, skip_serializing_if = "Option::is_none")]
408 pub plane_info: Option<ArtifactPlaneInfo>,
409 #[serde(default, skip_serializing_if = "Option::is_none")]
413 pub path_id: Option<ArtifactId>,
414 pub code_ref: CodeRef,
415 pub sketch_id: ObjectId,
417}
418
419#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
420#[ts(export_to = "Artifact.ts")]
421#[serde(rename_all = "camelCase")]
422pub enum SketchBlockConstraintType {
423 Angle,
424 Coincident,
425 Distance,
426 Diameter,
427 EqualRadius,
428 Fixed,
429 HorizontalDistance,
430 VerticalDistance,
431 Horizontal,
432 LinesEqualLength,
433 Midpoint,
434 Parallel,
435 Perpendicular,
436 Radius,
437 Symmetric,
438 Tangent,
439 Vertical,
440}
441
442#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
443#[ts(export_to = "Artifact.ts")]
444#[serde(rename_all = "camelCase")]
445pub struct SketchBlockConstraint {
446 pub id: ArtifactId,
447 pub sketch_id: ObjectId,
449 pub constraint_id: ObjectId,
451 pub constraint_type: SketchBlockConstraintType,
452 pub code_ref: CodeRef,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
456#[ts(export_to = "Artifact.ts")]
457#[serde(rename_all = "camelCase")]
458pub struct Wall {
459 pub id: ArtifactId,
460 pub seg_id: ArtifactId,
461 pub edge_cut_edge_ids: Vec<ArtifactId>,
462 pub sweep_id: ArtifactId,
463 pub path_ids: Vec<ArtifactId>,
464 pub face_code_ref: CodeRef,
467 pub cmd_id: Uuid,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
472#[ts(export_to = "Artifact.ts")]
473#[serde(rename_all = "camelCase")]
474pub struct Cap {
475 pub id: ArtifactId,
476 pub sub_type: CapSubType,
477 pub edge_cut_edge_ids: Vec<ArtifactId>,
478 pub sweep_id: ArtifactId,
479 pub path_ids: Vec<ArtifactId>,
480 pub face_code_ref: CodeRef,
483 pub cmd_id: Uuid,
485}
486
487#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
488#[ts(export_to = "Artifact.ts")]
489#[serde(rename_all = "camelCase")]
490pub enum CapSubType {
491 Start,
492 End,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
496#[ts(export_to = "Artifact.ts")]
497#[serde(rename_all = "camelCase")]
498pub struct SweepEdge {
499 pub id: ArtifactId,
500 pub sub_type: SweepEdgeSubType,
501 pub seg_id: ArtifactId,
502 pub cmd_id: Uuid,
503 #[serde(skip)]
505 pub index: usize,
506 pub sweep_id: ArtifactId,
507 pub common_surface_ids: Vec<ArtifactId>,
508}
509
510#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
511#[ts(export_to = "Artifact.ts")]
512#[serde(rename_all = "camelCase")]
513pub enum SweepEdgeSubType {
514 Opposite,
515 Adjacent,
516 PreviousAdjacent,
517}
518
519#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
520#[ts(export_to = "Artifact.ts")]
521#[serde(rename_all = "camelCase")]
522pub struct EdgeCut {
523 pub id: ArtifactId,
524 pub sub_type: EdgeCutSubType,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
528 pub source_selector_index: Option<usize>,
529 #[serde(default, skip_serializing_if = "Option::is_none")]
530 pub consumed_edge_id: Option<ArtifactId>,
531 pub edge_ids: Vec<ArtifactId>,
532 #[serde(default, skip_serializing_if = "Option::is_none")]
533 pub surface_id: Option<ArtifactId>,
534 pub code_ref: CodeRef,
535}
536
537#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
538#[ts(export_to = "Artifact.ts")]
539#[serde(rename_all = "camelCase")]
540pub enum EdgeCutSubType {
541 Fillet,
542 Chamfer,
543 Custom,
544}
545
546#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
547#[ts(export_to = "Artifact.ts")]
548#[serde(rename_all = "camelCase")]
549pub struct EdgeCutEdge {
550 pub id: ArtifactId,
551 pub edge_cut_id: ArtifactId,
552 pub surface_id: ArtifactId,
553}
554
555#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
556#[ts(export_to = "Artifact.ts")]
557#[serde(rename_all = "camelCase")]
558pub struct Helix {
559 pub id: ArtifactId,
560 pub axis_id: Option<ArtifactId>,
563 pub code_ref: CodeRef,
564 pub trajectory_sweep_id: Option<ArtifactId>,
566 pub consumed: bool,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
571#[ts(export_to = "Artifact.ts")]
572#[serde(rename_all = "camelCase")]
573pub struct ImportedGeometryArtifact {
574 pub id: ArtifactId,
575 pub code_ref: CodeRef,
576 #[serde(default)]
577 pub consumed: bool,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
581#[ts(export_to = "Artifact.ts")]
582#[serde(rename_all = "camelCase")]
583pub struct GdtAnnotationArtifact {
584 pub id: ArtifactId,
585 pub code_ref: CodeRef,
586 #[serde(default)]
587 pub consumed: bool,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
599#[ts(export_to = "Artifact.ts")]
600#[serde(rename_all = "camelCase")]
601pub struct NamedViewArtifact {
602 pub id: ArtifactId,
603 pub name: String,
606 #[ts(skip)]
609 pub declaring_module_id: ModuleId,
610 pub camera: ArtifactCameraView,
611 pub baseline: ArtifactVisibility,
614 pub show_ids: Vec<ArtifactId>,
617 pub hide_ids: Vec<ArtifactId>,
620 pub code_ref: CodeRef,
621}
622
623#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
624#[ts(export_to = "Artifact.ts")]
625#[serde(rename_all = "camelCase")]
626pub struct Pattern {
627 pub id: ArtifactId,
628 pub sub_type: PatternSubType,
629 pub source_id: ArtifactId,
631 pub copy_ids: Vec<ArtifactId>,
633 pub copy_face_ids: Vec<ArtifactId>,
635 pub copy_edge_ids: Vec<ArtifactId>,
637 pub code_ref: CodeRef,
638}
639
640#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
641#[ts(export_to = "Artifact.ts")]
642#[serde(rename_all = "camelCase")]
643pub enum PatternSubType {
644 Circular,
645 Linear,
646 Transform,
647}
648
649#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
650#[ts(export_to = "Artifact.ts")]
651#[serde(tag = "type", rename_all = "camelCase")]
652pub enum Artifact {
653 CompositeSolid(CompositeSolid),
654 Plane(Plane),
655 Path(Path),
656 Segment(Segment),
657 Solid2d(Solid2d),
658 PrimitiveFace(PrimitiveFace),
659 PrimitiveEdge(PrimitiveEdge),
660 PlaneOfFace(PlaneOfFace),
661 StartSketchOnFace(StartSketchOnFace),
662 StartSketchOnPlane(StartSketchOnPlane),
663 SketchBlock(SketchBlock),
664 SketchBlockConstraint(SketchBlockConstraint),
665 Sweep(Sweep),
666 Wall(Wall),
667 Cap(Cap),
668 SweepEdge(SweepEdge),
669 EdgeCut(EdgeCut),
670 EdgeCutEdge(EdgeCutEdge),
671 Helix(Helix),
672 ImportedGeometry(ImportedGeometryArtifact),
673 GdtAnnotation(GdtAnnotationArtifact),
674 NamedView(NamedViewArtifact),
675 Pattern(Pattern),
676}
677
678impl Artifact {
679 pub fn id(&self) -> ArtifactId {
680 match self {
681 Self::CompositeSolid(a) => a.id,
682 Self::Plane(a) => a.id,
683 Self::Path(a) => a.id,
684 Self::Segment(a) => a.id,
685 Self::Solid2d(a) => a.id,
686 Self::PrimitiveFace(a) => a.id,
687 Self::PrimitiveEdge(a) => a.id,
688 Self::PlaneOfFace(a) => a.id,
689 Self::StartSketchOnFace(a) => a.id,
690 Self::StartSketchOnPlane(a) => a.id,
691 Self::SketchBlock(a) => a.id,
692 Self::SketchBlockConstraint(a) => a.id,
693 Self::Sweep(a) => a.id,
694 Self::Wall(a) => a.id,
695 Self::Cap(a) => a.id,
696 Self::SweepEdge(a) => a.id,
697 Self::EdgeCut(a) => a.id,
698 Self::EdgeCutEdge(a) => a.id,
699 Self::Helix(a) => a.id,
700 Self::ImportedGeometry(a) => a.id,
701 Self::GdtAnnotation(a) => a.id,
702 Self::NamedView(a) => a.id,
703 Self::Pattern(a) => a.id,
704 }
705 }
706
707 pub fn code_ref(&self) -> Option<&CodeRef> {
710 match self {
711 Self::CompositeSolid(a) => Some(&a.code_ref),
712 Self::Plane(a) => Some(&a.code_ref),
713 Self::Path(a) => Some(&a.code_ref),
714 Self::Segment(a) => Some(&a.code_ref),
715 Self::Solid2d(_) => None,
716 Self::PrimitiveFace(a) => Some(&a.code_ref),
717 Self::PrimitiveEdge(a) => Some(&a.code_ref),
718 Self::PlaneOfFace(a) => Some(&a.code_ref),
719 Self::StartSketchOnFace(a) => Some(&a.code_ref),
720 Self::StartSketchOnPlane(a) => Some(&a.code_ref),
721 Self::SketchBlock(a) => Some(&a.code_ref),
722 Self::SketchBlockConstraint(a) => Some(&a.code_ref),
723 Self::Sweep(a) => Some(&a.code_ref),
724 Self::Wall(_) | Self::Cap(_) | Self::SweepEdge(_) => None,
725 Self::EdgeCut(a) => Some(&a.code_ref),
726 Self::EdgeCutEdge(_) => None,
727 Self::Helix(a) => Some(&a.code_ref),
728 Self::ImportedGeometry(a) => Some(&a.code_ref),
729 Self::GdtAnnotation(a) => Some(&a.code_ref),
730 Self::NamedView(a) => Some(&a.code_ref),
731 Self::Pattern(a) => Some(&a.code_ref),
732 }
733 }
734
735 pub fn face_code_ref(&self) -> Option<&CodeRef> {
738 match self {
739 Self::PrimitiveFace(a) => Some(&a.code_ref),
740 Self::Wall(a) => Some(&a.face_code_ref),
741 Self::Cap(a) => Some(&a.face_code_ref),
742 _ => None,
743 }
744 }
745}
746
747#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, ts_rs::TS)]
748#[ts(export_to = "Artifact.ts")]
749#[serde(rename_all = "camelCase")]
750pub struct ArtifactGraph {
751 map: IndexMap<ArtifactId, Artifact>,
752 item_count: usize,
753}
754
755impl ArtifactGraph {
756 pub fn from_parts(map: IndexMap<ArtifactId, Artifact>, item_count: usize) -> Self {
757 Self { map, item_count }
758 }
759
760 pub fn into_parts(self) -> (IndexMap<ArtifactId, Artifact>, usize) {
761 (self.map, self.item_count)
762 }
763
764 pub fn item_count(&self) -> usize {
765 self.item_count
766 }
767
768 pub fn get(&self, id: &ArtifactId) -> Option<&Artifact> {
769 self.map.get(id)
770 }
771
772 pub fn len(&self) -> usize {
773 self.map.len()
774 }
775
776 pub fn is_empty(&self) -> bool {
777 self.map.is_empty()
778 }
779
780 pub fn iter(&self) -> impl Iterator<Item = (&ArtifactId, &Artifact)> {
781 self.map.iter()
782 }
783
784 pub fn values(&self) -> impl Iterator<Item = &Artifact> {
785 self.map.values()
786 }
787
788 pub fn clear(&mut self) {
789 self.map.clear();
790 self.item_count = 0;
791 }
792}
793
794#[cfg(test)]
795mod tests {
796 use super::*;
797
798 #[test]
799 fn default_artifact_graph_json_round_trip() {
800 let graph = ArtifactGraph::default();
801 let json = serde_json::to_string(&graph).unwrap();
802
803 assert_eq!(json, r#"{"map":{},"itemCount":0}"#);
804 assert_eq!(serde_json::from_str::<ArtifactGraph>(&json).unwrap(), graph);
805 }
806
807 #[test]
808 fn artifact_sweep_method_uses_engine_json_shape() {
809 assert_eq!(serde_json::to_string(&ArtifactSweepMethod::New).unwrap(), r#""new""#);
810 assert_eq!(
811 serde_json::to_string(&ArtifactSweepMethod::Merge).unwrap(),
812 r#""merge""#
813 );
814 }
815}