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