Skip to main content

axiolid_model/
value.rs

1//! Closed conversions from built-in representations into graph nodes.
2//!
3//! Backend/provider traits remain intentionally open. This trait is sealed
4//! because `GeometryNode` is a closed built-in vocabulary: downstream types
5//! must first translate into one of these neutral representations.
6
7use axiolid_core::{Aabb, Frame2, Frame3, Transform3};
8use axiolid_curve::{Curve2, Curve3};
9use axiolid_mesh::{PolygonMesh, TriMesh};
10use axiolid_primitive::{HalfSpace, Primitive};
11use axiolid_profile::Profile;
12use axiolid_surface::Surface;
13use axiolid_topology::BRep;
14
15use crate::{
16    CurveRelation, GeometryNode, Instance, NodeId, OpenProfile, PointOnCurve, PointOnSurface,
17    SolidOperation, SurfaceRelation,
18};
19
20mod private {
21    pub trait Sealed {}
22}
23
24/// A built-in neutral value that can be inserted into a [`crate::GeometryGraph`].
25///
26/// This is sealed deliberately. Third-party kernels extend execution through
27/// the open traits in operation-contract packages; source adapters extend input
28/// by translating their values into one of the canonical node families.
29///
30/// ```compile_fail
31/// use axiolid_model::{BuiltInNode, GeometryNode};
32///
33/// struct ForeignNode;
34///
35/// impl Into<GeometryNode> for ForeignNode {
36///     fn into(self) -> GeometryNode {
37///         panic!("compile-only example")
38///     }
39/// }
40///
41/// // Rejected because the private sealing trait cannot be implemented here.
42/// impl BuiltInNode for ForeignNode {}
43/// ```
44pub trait BuiltInNode: private::Sealed + Into<GeometryNode> {}
45
46macro_rules! built_in_node {
47    ($value:ty, $variant:ident) => {
48        impl From<$value> for GeometryNode {
49            fn from(value: $value) -> Self {
50                Self::$variant(value)
51            }
52        }
53
54        impl private::Sealed for $value {}
55        impl BuiltInNode for $value {}
56    };
57}
58
59built_in_node!(Frame2, Frame2);
60built_in_node!(Frame3, Frame3);
61built_in_node!(Transform3, Transform);
62built_in_node!(Curve2, Curve2);
63built_in_node!(Curve3, Curve3);
64built_in_node!(CurveRelation, CurveRelation);
65built_in_node!(PointOnCurve, PointOnCurve);
66built_in_node!(Surface, Surface);
67built_in_node!(SurfaceRelation, SurfaceRelation);
68built_in_node!(PointOnSurface, PointOnSurface);
69built_in_node!(Profile, Profile);
70built_in_node!(OpenProfile, OpenProfile);
71built_in_node!(Primitive, Primitive);
72built_in_node!(HalfSpace, HalfSpace);
73built_in_node!(SolidOperation, SolidOperation);
74built_in_node!(BRep<NodeId>, BRep);
75built_in_node!(PolygonMesh, PolygonMesh);
76built_in_node!(TriMesh, TriMesh);
77built_in_node!(Aabb, BoundingBox);
78built_in_node!(Instance, Instance);
79
80#[cfg(test)]
81mod tests {
82    use axiolid_primitive::Primitive;
83
84    use super::*;
85
86    fn accepts_built_in<T: BuiltInNode>(value: T) -> GeometryNode {
87        value.into()
88    }
89
90    #[test]
91    fn built_in_values_convert_without_format_knowledge() {
92        let node = accepts_built_in(Primitive::Sphere { radius: 2.0 });
93        assert!(matches!(
94            node,
95            GeometryNode::Primitive(Primitive::Sphere { radius: 2.0 })
96        ));
97    }
98}