codama_nodes/type_nodes/
sentinel_type_node.rs

1use crate::{
2    ConstantValueNode, NestedTypeNode, NestedTypeNodeTrait, TypeNode, TypeNodeTrait,
3    TypeNodeUnionTrait,
4};
5use codama_errors::{CodamaError, CodamaResult};
6use codama_nodes_derive::nestable_type_node;
7
8#[nestable_type_node]
9pub struct SentinelTypeNode<T: TypeNodeUnionTrait> {
10    // Children.
11    #[serde(bound = "T: TypeNodeUnionTrait")]
12    pub r#type: Box<T>,
13    pub sentinel: ConstantValueNode,
14}
15
16impl From<SentinelTypeNode<crate::TypeNode>> for crate::Node {
17    fn from(val: SentinelTypeNode<crate::TypeNode>) -> Self {
18        crate::Node::Type(val.into())
19    }
20}
21
22impl<T: TypeNodeTrait> From<SentinelTypeNode<NestedTypeNode<T>>> for SentinelTypeNode<TypeNode> {
23    fn from(node: SentinelTypeNode<NestedTypeNode<T>>) -> Self {
24        SentinelTypeNode {
25            r#type: Box::new(TypeNode::from(*node.r#type)),
26            sentinel: node.sentinel,
27        }
28    }
29}
30
31impl<T: TypeNodeTrait> TryFrom<SentinelTypeNode<TypeNode>> for SentinelTypeNode<NestedTypeNode<T>> {
32    type Error = CodamaError;
33    fn try_from(node: SentinelTypeNode<TypeNode>) -> CodamaResult<Self> {
34        Ok(SentinelTypeNode {
35            r#type: Box::new(NestedTypeNode::try_from(*node.r#type)?),
36            sentinel: node.sentinel,
37        })
38    }
39}
40
41impl<T: TypeNodeUnionTrait> SentinelTypeNode<T> {
42    pub fn new<U>(r#type: U, sentinel: ConstantValueNode) -> Self
43    where
44        U: Into<T>,
45    {
46        Self {
47            r#type: Box::new(r#type.into()),
48            sentinel,
49        }
50    }
51}
52
53impl<T: TypeNodeTrait> NestedTypeNodeTrait<T> for SentinelTypeNode<NestedTypeNode<T>> {
54    type Mapped<U: TypeNodeTrait> = SentinelTypeNode<NestedTypeNode<U>>;
55
56    fn get_nested_type_node(&self) -> &T {
57        self.r#type.get_nested_type_node()
58    }
59
60    fn try_map_nested_type_node<U: TypeNodeTrait, F: FnOnce(T) -> CodamaResult<U>>(
61        self,
62        f: F,
63    ) -> CodamaResult<Self::Mapped<U>> {
64        Ok(SentinelTypeNode {
65            r#type: Box::new(self.r#type.try_map_nested_type_node(f)?),
66            sentinel: self.sentinel,
67        })
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::{Base16, NestedTypeNode, StringTypeNode, TypeNode};
75
76    #[test]
77    fn new_type_node() {
78        let node = SentinelTypeNode::<TypeNode>::new(
79            StringTypeNode::utf8(),
80            ConstantValueNode::bytes(Base16, "ffff"),
81        );
82        assert_eq!(*node.r#type, TypeNode::String(StringTypeNode::utf8()));
83        assert_eq!(node.sentinel, ConstantValueNode::bytes(Base16, "ffff"));
84    }
85
86    #[test]
87    fn new_nested_type_node() {
88        let node = SentinelTypeNode::<NestedTypeNode<StringTypeNode>>::new(
89            StringTypeNode::utf8(),
90            ConstantValueNode::bytes(Base16, "ffff"),
91        );
92        assert_eq!(*node.r#type, NestedTypeNode::Value(StringTypeNode::utf8()));
93        assert_eq!(node.get_nested_type_node(), &StringTypeNode::utf8());
94        assert_eq!(node.sentinel, ConstantValueNode::bytes(Base16, "ffff"));
95    }
96
97    #[test]
98    fn to_json() {
99        let node = SentinelTypeNode::<TypeNode>::new(
100            StringTypeNode::utf8(),
101            ConstantValueNode::bytes(Base16, "ffff"),
102        );
103        let json = serde_json::to_string(&node).unwrap();
104        assert_eq!(
105            json,
106            r#"{"kind":"sentinelTypeNode","type":{"kind":"stringTypeNode","encoding":"utf8"},"sentinel":{"kind":"constantValueNode","type":{"kind":"bytesTypeNode"},"value":{"kind":"bytesValueNode","data":"ffff","encoding":"base16"}}}"#
107        );
108    }
109
110    #[test]
111    fn from_json() {
112        let json = r#"{"kind":"sentinelTypeNode","type":{"kind":"stringTypeNode","encoding":"utf8"},"sentinel":{"kind":"constantValueNode","type":{"kind":"bytesTypeNode"},"value":{"kind":"bytesValueNode","data":"ffff","encoding":"base16"}}}"#;
113        let node: SentinelTypeNode<TypeNode> = serde_json::from_str(json).unwrap();
114        assert_eq!(
115            node,
116            SentinelTypeNode::<TypeNode>::new(
117                StringTypeNode::utf8(),
118                ConstantValueNode::bytes(Base16, "ffff"),
119            )
120        );
121    }
122}