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 pub path_id: ArtifactId,
308 pub surface_ids: Vec<ArtifactId>,
309 pub edge_ids: Vec<ArtifactId>,
310 pub code_ref: CodeRef,
311 #[serde(default, skip_serializing_if = "Option::is_none")]
314 pub source_sweep_id: Option<ArtifactId>,
315 pub trajectory_id: Option<ArtifactId>,
319 pub method: ArtifactSweepMethod,
320 pub consumed: bool,
322 #[serde(default, skip_serializing_if = "Vec::is_empty")]
324 pub pattern_ids: Vec<ArtifactId>,
325}
326
327#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
328#[ts(export_to = "Artifact.ts")]
329#[serde(rename_all = "camelCase")]
330pub enum SweepSubType {
331 Extrusion,
332 ExtrusionTwist,
333 Revolve,
334 RevolveAboutEdge,
335 Loft,
336 Blend,
337 Sweep,
338}
339
340#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
341#[ts(export_to = "Artifact.ts")]
342#[serde(rename_all = "camelCase")]
343pub struct Solid2d {
344 pub id: ArtifactId,
345 pub path_id: ArtifactId,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
349#[ts(export_to = "Artifact.ts")]
350#[serde(rename_all = "camelCase")]
351pub struct PrimitiveFace {
352 pub id: ArtifactId,
353 pub solid_id: ArtifactId,
354 pub code_ref: CodeRef,
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
358#[ts(export_to = "Artifact.ts")]
359#[serde(rename_all = "camelCase")]
360pub struct PrimitiveEdge {
361 pub id: ArtifactId,
362 pub solid_id: ArtifactId,
363 pub code_ref: CodeRef,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
367#[ts(export_to = "Artifact.ts")]
368#[serde(rename_all = "camelCase")]
369pub struct PlaneOfFace {
370 pub id: ArtifactId,
371 pub face_id: ArtifactId,
372 pub code_ref: CodeRef,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
376#[ts(export_to = "Artifact.ts")]
377#[serde(rename_all = "camelCase")]
378pub struct StartSketchOnFace {
379 pub id: ArtifactId,
380 pub face_id: ArtifactId,
381 pub code_ref: CodeRef,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
385#[ts(export_to = "Artifact.ts")]
386#[serde(rename_all = "camelCase")]
387pub struct StartSketchOnPlane {
388 pub id: ArtifactId,
389 pub plane_id: ArtifactId,
390 pub code_ref: CodeRef,
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
394#[ts(export_to = "Artifact.ts")]
395#[serde(rename_all = "camelCase")]
396pub struct SketchBlock {
397 pub id: ArtifactId,
398 #[serde(default, skip_serializing_if = "Option::is_none")]
400 pub standard_plane: Option<PlaneName>,
401 #[serde(default, skip_serializing_if = "Option::is_none")]
403 pub plane_id: Option<ArtifactId>,
404 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub plane_info: Option<ArtifactPlaneInfo>,
407 #[serde(default, skip_serializing_if = "Option::is_none")]
411 pub path_id: Option<ArtifactId>,
412 pub code_ref: CodeRef,
413 pub sketch_id: ObjectId,
415}
416
417#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
418#[ts(export_to = "Artifact.ts")]
419#[serde(rename_all = "camelCase")]
420pub enum SketchBlockConstraintType {
421 Angle,
422 Coincident,
423 Distance,
424 Diameter,
425 EqualRadius,
426 Fixed,
427 HorizontalDistance,
428 VerticalDistance,
429 Horizontal,
430 LinesEqualLength,
431 Midpoint,
432 Parallel,
433 Perpendicular,
434 Radius,
435 Symmetric,
436 Tangent,
437 Vertical,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
441#[ts(export_to = "Artifact.ts")]
442#[serde(rename_all = "camelCase")]
443pub struct SketchBlockConstraint {
444 pub id: ArtifactId,
445 pub sketch_id: ObjectId,
447 pub constraint_id: ObjectId,
449 pub constraint_type: SketchBlockConstraintType,
450 pub code_ref: CodeRef,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
454#[ts(export_to = "Artifact.ts")]
455#[serde(rename_all = "camelCase")]
456pub struct Wall {
457 pub id: ArtifactId,
458 pub seg_id: ArtifactId,
459 pub edge_cut_edge_ids: Vec<ArtifactId>,
460 pub sweep_id: ArtifactId,
461 pub path_ids: Vec<ArtifactId>,
462 pub face_code_ref: CodeRef,
465 pub cmd_id: Uuid,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
470#[ts(export_to = "Artifact.ts")]
471#[serde(rename_all = "camelCase")]
472pub struct Cap {
473 pub id: ArtifactId,
474 pub sub_type: CapSubType,
475 pub edge_cut_edge_ids: Vec<ArtifactId>,
476 pub sweep_id: ArtifactId,
477 pub path_ids: Vec<ArtifactId>,
478 pub face_code_ref: CodeRef,
481 pub cmd_id: Uuid,
483}
484
485#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
486#[ts(export_to = "Artifact.ts")]
487#[serde(rename_all = "camelCase")]
488pub enum CapSubType {
489 Start,
490 End,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
494#[ts(export_to = "Artifact.ts")]
495#[serde(rename_all = "camelCase")]
496pub struct SweepEdge {
497 pub id: ArtifactId,
498 pub sub_type: SweepEdgeSubType,
499 pub seg_id: ArtifactId,
500 pub cmd_id: Uuid,
501 #[serde(skip)]
503 pub index: usize,
504 pub sweep_id: ArtifactId,
505 pub common_surface_ids: Vec<ArtifactId>,
506}
507
508#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
509#[ts(export_to = "Artifact.ts")]
510#[serde(rename_all = "camelCase")]
511pub enum SweepEdgeSubType {
512 Opposite,
513 Adjacent,
514 PreviousAdjacent,
515}
516
517#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
518#[ts(export_to = "Artifact.ts")]
519#[serde(rename_all = "camelCase")]
520pub struct EdgeCut {
521 pub id: ArtifactId,
522 pub sub_type: EdgeCutSubType,
523 pub consumed_edge_id: ArtifactId,
524 pub edge_ids: Vec<ArtifactId>,
525 #[serde(default, skip_serializing_if = "Option::is_none")]
526 pub surface_id: Option<ArtifactId>,
527 pub code_ref: CodeRef,
528}
529
530#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
531#[ts(export_to = "Artifact.ts")]
532#[serde(rename_all = "camelCase")]
533pub enum EdgeCutSubType {
534 Fillet,
535 Chamfer,
536 Custom,
537}
538
539#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
540#[ts(export_to = "Artifact.ts")]
541#[serde(rename_all = "camelCase")]
542pub struct EdgeCutEdge {
543 pub id: ArtifactId,
544 pub edge_cut_id: ArtifactId,
545 pub surface_id: ArtifactId,
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
549#[ts(export_to = "Artifact.ts")]
550#[serde(rename_all = "camelCase")]
551pub struct Helix {
552 pub id: ArtifactId,
553 pub axis_id: Option<ArtifactId>,
556 pub code_ref: CodeRef,
557 pub trajectory_sweep_id: Option<ArtifactId>,
559 pub consumed: bool,
561}
562
563#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
564#[ts(export_to = "Artifact.ts")]
565#[serde(rename_all = "camelCase")]
566pub struct ImportedGeometryArtifact {
567 pub id: ArtifactId,
568 pub code_ref: CodeRef,
569 #[serde(default)]
570 pub consumed: bool,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
574#[ts(export_to = "Artifact.ts")]
575#[serde(rename_all = "camelCase")]
576pub struct GdtAnnotationArtifact {
577 pub id: ArtifactId,
578 pub code_ref: CodeRef,
579 #[serde(default)]
580 pub consumed: bool,
581}
582
583#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
592#[ts(export_to = "Artifact.ts")]
593#[serde(rename_all = "camelCase")]
594pub struct NamedViewArtifact {
595 pub id: ArtifactId,
596 pub name: String,
599 pub camera: ArtifactCameraView,
600 pub baseline: ArtifactVisibility,
603 pub show_ids: Vec<ArtifactId>,
606 pub hide_ids: Vec<ArtifactId>,
609 pub code_ref: CodeRef,
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
613#[ts(export_to = "Artifact.ts")]
614#[serde(rename_all = "camelCase")]
615pub struct Pattern {
616 pub id: ArtifactId,
617 pub sub_type: PatternSubType,
618 pub source_id: ArtifactId,
620 pub copy_ids: Vec<ArtifactId>,
622 pub copy_face_ids: Vec<ArtifactId>,
624 pub copy_edge_ids: Vec<ArtifactId>,
626 pub code_ref: CodeRef,
627}
628
629#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
630#[ts(export_to = "Artifact.ts")]
631#[serde(rename_all = "camelCase")]
632pub enum PatternSubType {
633 Circular,
634 Linear,
635 Transform,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
639#[ts(export_to = "Artifact.ts")]
640#[serde(tag = "type", rename_all = "camelCase")]
641pub enum Artifact {
642 CompositeSolid(CompositeSolid),
643 Plane(Plane),
644 Path(Path),
645 Segment(Segment),
646 Solid2d(Solid2d),
647 PrimitiveFace(PrimitiveFace),
648 PrimitiveEdge(PrimitiveEdge),
649 PlaneOfFace(PlaneOfFace),
650 StartSketchOnFace(StartSketchOnFace),
651 StartSketchOnPlane(StartSketchOnPlane),
652 SketchBlock(SketchBlock),
653 SketchBlockConstraint(SketchBlockConstraint),
654 Sweep(Sweep),
655 Wall(Wall),
656 Cap(Cap),
657 SweepEdge(SweepEdge),
658 EdgeCut(EdgeCut),
659 EdgeCutEdge(EdgeCutEdge),
660 Helix(Helix),
661 ImportedGeometry(ImportedGeometryArtifact),
662 GdtAnnotation(GdtAnnotationArtifact),
663 NamedView(NamedViewArtifact),
664 Pattern(Pattern),
665}
666
667impl Artifact {
668 pub fn id(&self) -> ArtifactId {
669 match self {
670 Self::CompositeSolid(a) => a.id,
671 Self::Plane(a) => a.id,
672 Self::Path(a) => a.id,
673 Self::Segment(a) => a.id,
674 Self::Solid2d(a) => a.id,
675 Self::PrimitiveFace(a) => a.id,
676 Self::PrimitiveEdge(a) => a.id,
677 Self::PlaneOfFace(a) => a.id,
678 Self::StartSketchOnFace(a) => a.id,
679 Self::StartSketchOnPlane(a) => a.id,
680 Self::SketchBlock(a) => a.id,
681 Self::SketchBlockConstraint(a) => a.id,
682 Self::Sweep(a) => a.id,
683 Self::Wall(a) => a.id,
684 Self::Cap(a) => a.id,
685 Self::SweepEdge(a) => a.id,
686 Self::EdgeCut(a) => a.id,
687 Self::EdgeCutEdge(a) => a.id,
688 Self::Helix(a) => a.id,
689 Self::ImportedGeometry(a) => a.id,
690 Self::GdtAnnotation(a) => a.id,
691 Self::NamedView(a) => a.id,
692 Self::Pattern(a) => a.id,
693 }
694 }
695
696 pub fn code_ref(&self) -> Option<&CodeRef> {
699 match self {
700 Self::CompositeSolid(a) => Some(&a.code_ref),
701 Self::Plane(a) => Some(&a.code_ref),
702 Self::Path(a) => Some(&a.code_ref),
703 Self::Segment(a) => Some(&a.code_ref),
704 Self::Solid2d(_) => None,
705 Self::PrimitiveFace(a) => Some(&a.code_ref),
706 Self::PrimitiveEdge(a) => Some(&a.code_ref),
707 Self::PlaneOfFace(a) => Some(&a.code_ref),
708 Self::StartSketchOnFace(a) => Some(&a.code_ref),
709 Self::StartSketchOnPlane(a) => Some(&a.code_ref),
710 Self::SketchBlock(a) => Some(&a.code_ref),
711 Self::SketchBlockConstraint(a) => Some(&a.code_ref),
712 Self::Sweep(a) => Some(&a.code_ref),
713 Self::Wall(_) | Self::Cap(_) | Self::SweepEdge(_) => None,
714 Self::EdgeCut(a) => Some(&a.code_ref),
715 Self::EdgeCutEdge(_) => None,
716 Self::Helix(a) => Some(&a.code_ref),
717 Self::ImportedGeometry(a) => Some(&a.code_ref),
718 Self::GdtAnnotation(a) => Some(&a.code_ref),
719 Self::NamedView(a) => Some(&a.code_ref),
720 Self::Pattern(a) => Some(&a.code_ref),
721 }
722 }
723
724 pub fn face_code_ref(&self) -> Option<&CodeRef> {
727 match self {
728 Self::PrimitiveFace(a) => Some(&a.code_ref),
729 Self::Wall(a) => Some(&a.face_code_ref),
730 Self::Cap(a) => Some(&a.face_code_ref),
731 _ => None,
732 }
733 }
734}
735
736#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, ts_rs::TS)]
737#[ts(export_to = "Artifact.ts")]
738#[serde(rename_all = "camelCase")]
739pub struct ArtifactGraph {
740 map: IndexMap<ArtifactId, Artifact>,
741 item_count: usize,
742}
743
744impl ArtifactGraph {
745 pub fn from_parts(map: IndexMap<ArtifactId, Artifact>, item_count: usize) -> Self {
746 Self { map, item_count }
747 }
748
749 pub fn into_parts(self) -> (IndexMap<ArtifactId, Artifact>, usize) {
750 (self.map, self.item_count)
751 }
752
753 pub fn item_count(&self) -> usize {
754 self.item_count
755 }
756
757 pub fn get(&self, id: &ArtifactId) -> Option<&Artifact> {
758 self.map.get(id)
759 }
760
761 pub fn len(&self) -> usize {
762 self.map.len()
763 }
764
765 pub fn is_empty(&self) -> bool {
766 self.map.is_empty()
767 }
768
769 pub fn iter(&self) -> impl Iterator<Item = (&ArtifactId, &Artifact)> {
770 self.map.iter()
771 }
772
773 pub fn values(&self) -> impl Iterator<Item = &Artifact> {
774 self.map.values()
775 }
776
777 pub fn clear(&mut self) {
778 self.map.clear();
779 self.item_count = 0;
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786
787 #[test]
788 fn default_artifact_graph_json_round_trip() {
789 let graph = ArtifactGraph::default();
790 let json = serde_json::to_string(&graph).unwrap();
791
792 assert_eq!(json, r#"{"map":{},"itemCount":0}"#);
793 assert_eq!(serde_json::from_str::<ArtifactGraph>(&json).unwrap(), graph);
794 }
795
796 #[test]
797 fn artifact_sweep_method_uses_engine_json_shape() {
798 assert_eq!(serde_json::to_string(&ArtifactSweepMethod::New).unwrap(), r#""new""#);
799 assert_eq!(
800 serde_json::to_string(&ArtifactSweepMethod::Merge).unwrap(),
801 r#""merge""#
802 );
803 }
804}