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}
570
571#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
572#[ts(export_to = "Artifact.ts")]
573#[serde(rename_all = "camelCase")]
574pub struct GdtAnnotationArtifact {
575 pub id: ArtifactId,
576 pub code_ref: CodeRef,
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
588#[ts(export_to = "Artifact.ts")]
589#[serde(rename_all = "camelCase")]
590pub struct NamedViewArtifact {
591 pub id: ArtifactId,
592 pub name: String,
595 pub camera: ArtifactCameraView,
596 pub baseline: ArtifactVisibility,
599 pub show_ids: Vec<ArtifactId>,
602 pub hide_ids: Vec<ArtifactId>,
605 pub code_ref: CodeRef,
606}
607
608#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
609#[ts(export_to = "Artifact.ts")]
610#[serde(rename_all = "camelCase")]
611pub struct Pattern {
612 pub id: ArtifactId,
613 pub sub_type: PatternSubType,
614 pub source_id: ArtifactId,
616 pub copy_ids: Vec<ArtifactId>,
618 pub copy_face_ids: Vec<ArtifactId>,
620 pub copy_edge_ids: Vec<ArtifactId>,
622 pub code_ref: CodeRef,
623}
624
625#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema, PartialEq, Eq, ts_rs::TS)]
626#[ts(export_to = "Artifact.ts")]
627#[serde(rename_all = "camelCase")]
628pub enum PatternSubType {
629 Circular,
630 Linear,
631 Transform,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, ts_rs::TS)]
635#[ts(export_to = "Artifact.ts")]
636#[serde(tag = "type", rename_all = "camelCase")]
637pub enum Artifact {
638 CompositeSolid(CompositeSolid),
639 Plane(Plane),
640 Path(Path),
641 Segment(Segment),
642 Solid2d(Solid2d),
643 PrimitiveFace(PrimitiveFace),
644 PrimitiveEdge(PrimitiveEdge),
645 PlaneOfFace(PlaneOfFace),
646 StartSketchOnFace(StartSketchOnFace),
647 StartSketchOnPlane(StartSketchOnPlane),
648 SketchBlock(SketchBlock),
649 SketchBlockConstraint(SketchBlockConstraint),
650 Sweep(Sweep),
651 Wall(Wall),
652 Cap(Cap),
653 SweepEdge(SweepEdge),
654 EdgeCut(EdgeCut),
655 EdgeCutEdge(EdgeCutEdge),
656 Helix(Helix),
657 ImportedGeometry(ImportedGeometryArtifact),
658 GdtAnnotation(GdtAnnotationArtifact),
659 NamedView(NamedViewArtifact),
660 Pattern(Pattern),
661}
662
663impl Artifact {
664 pub fn id(&self) -> ArtifactId {
665 match self {
666 Self::CompositeSolid(a) => a.id,
667 Self::Plane(a) => a.id,
668 Self::Path(a) => a.id,
669 Self::Segment(a) => a.id,
670 Self::Solid2d(a) => a.id,
671 Self::PrimitiveFace(a) => a.id,
672 Self::PrimitiveEdge(a) => a.id,
673 Self::PlaneOfFace(a) => a.id,
674 Self::StartSketchOnFace(a) => a.id,
675 Self::StartSketchOnPlane(a) => a.id,
676 Self::SketchBlock(a) => a.id,
677 Self::SketchBlockConstraint(a) => a.id,
678 Self::Sweep(a) => a.id,
679 Self::Wall(a) => a.id,
680 Self::Cap(a) => a.id,
681 Self::SweepEdge(a) => a.id,
682 Self::EdgeCut(a) => a.id,
683 Self::EdgeCutEdge(a) => a.id,
684 Self::Helix(a) => a.id,
685 Self::ImportedGeometry(a) => a.id,
686 Self::GdtAnnotation(a) => a.id,
687 Self::NamedView(a) => a.id,
688 Self::Pattern(a) => a.id,
689 }
690 }
691
692 pub fn code_ref(&self) -> Option<&CodeRef> {
695 match self {
696 Self::CompositeSolid(a) => Some(&a.code_ref),
697 Self::Plane(a) => Some(&a.code_ref),
698 Self::Path(a) => Some(&a.code_ref),
699 Self::Segment(a) => Some(&a.code_ref),
700 Self::Solid2d(_) => None,
701 Self::PrimitiveFace(a) => Some(&a.code_ref),
702 Self::PrimitiveEdge(a) => Some(&a.code_ref),
703 Self::PlaneOfFace(a) => Some(&a.code_ref),
704 Self::StartSketchOnFace(a) => Some(&a.code_ref),
705 Self::StartSketchOnPlane(a) => Some(&a.code_ref),
706 Self::SketchBlock(a) => Some(&a.code_ref),
707 Self::SketchBlockConstraint(a) => Some(&a.code_ref),
708 Self::Sweep(a) => Some(&a.code_ref),
709 Self::Wall(_) | Self::Cap(_) | Self::SweepEdge(_) => None,
710 Self::EdgeCut(a) => Some(&a.code_ref),
711 Self::EdgeCutEdge(_) => None,
712 Self::Helix(a) => Some(&a.code_ref),
713 Self::ImportedGeometry(a) => Some(&a.code_ref),
714 Self::GdtAnnotation(a) => Some(&a.code_ref),
715 Self::NamedView(a) => Some(&a.code_ref),
716 Self::Pattern(a) => Some(&a.code_ref),
717 }
718 }
719
720 pub fn face_code_ref(&self) -> Option<&CodeRef> {
723 match self {
724 Self::PrimitiveFace(a) => Some(&a.code_ref),
725 Self::Wall(a) => Some(&a.face_code_ref),
726 Self::Cap(a) => Some(&a.face_code_ref),
727 _ => None,
728 }
729 }
730}
731
732#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema, ts_rs::TS)]
733#[ts(export_to = "Artifact.ts")]
734#[serde(rename_all = "camelCase")]
735pub struct ArtifactGraph {
736 map: IndexMap<ArtifactId, Artifact>,
737 item_count: usize,
738}
739
740impl ArtifactGraph {
741 pub fn from_parts(map: IndexMap<ArtifactId, Artifact>, item_count: usize) -> Self {
742 Self { map, item_count }
743 }
744
745 pub fn into_parts(self) -> (IndexMap<ArtifactId, Artifact>, usize) {
746 (self.map, self.item_count)
747 }
748
749 pub fn item_count(&self) -> usize {
750 self.item_count
751 }
752
753 pub fn get(&self, id: &ArtifactId) -> Option<&Artifact> {
754 self.map.get(id)
755 }
756
757 pub fn len(&self) -> usize {
758 self.map.len()
759 }
760
761 pub fn is_empty(&self) -> bool {
762 self.map.is_empty()
763 }
764
765 pub fn iter(&self) -> impl Iterator<Item = (&ArtifactId, &Artifact)> {
766 self.map.iter()
767 }
768
769 pub fn values(&self) -> impl Iterator<Item = &Artifact> {
770 self.map.values()
771 }
772
773 pub fn clear(&mut self) {
774 self.map.clear();
775 self.item_count = 0;
776 }
777}
778
779#[cfg(test)]
780mod tests {
781 use super::*;
782
783 #[test]
784 fn default_artifact_graph_json_round_trip() {
785 let graph = ArtifactGraph::default();
786 let json = serde_json::to_string(&graph).unwrap();
787
788 assert_eq!(json, r#"{"map":{},"itemCount":0}"#);
789 assert_eq!(serde_json::from_str::<ArtifactGraph>(&json).unwrap(), graph);
790 }
791
792 #[test]
793 fn artifact_sweep_method_uses_engine_json_shape() {
794 assert_eq!(serde_json::to_string(&ArtifactSweepMethod::New).unwrap(), r#""new""#);
795 assert_eq!(
796 serde_json::to_string(&ArtifactSweepMethod::Merge).unwrap(),
797 r#""merge""#
798 );
799 }
800}