1use crate::{NestedTypeNode, NestedTypeNodeTrait, TypeNode, TypeNodeTrait, TypeNodeUnionTrait};
2use codama_errors::{CodamaError, CodamaResult};
3use codama_nodes_derive::nestable_type_node;
4use serde::{Deserialize, Serialize};
5
6#[nestable_type_node]
7pub struct PreOffsetTypeNode<T: TypeNodeUnionTrait> {
8 pub offset: i32,
10 pub strategy: PreOffsetStrategy,
11
12 #[serde(bound = "T: TypeNodeUnionTrait")]
14 pub r#type: Box<T>,
15}
16
17impl From<PreOffsetTypeNode<crate::TypeNode>> for crate::Node {
18 fn from(val: PreOffsetTypeNode<crate::TypeNode>) -> Self {
19 crate::Node::Type(val.into())
20 }
21}
22
23impl<T: TypeNodeTrait> From<PreOffsetTypeNode<NestedTypeNode<T>>> for PreOffsetTypeNode<TypeNode> {
24 fn from(node: PreOffsetTypeNode<NestedTypeNode<T>>) -> Self {
25 PreOffsetTypeNode {
26 strategy: node.strategy,
27 offset: node.offset,
28 r#type: Box::new(TypeNode::from(*node.r#type)),
29 }
30 }
31}
32
33impl<T: TypeNodeTrait> TryFrom<PreOffsetTypeNode<TypeNode>>
34 for PreOffsetTypeNode<NestedTypeNode<T>>
35{
36 type Error = CodamaError;
37 fn try_from(node: PreOffsetTypeNode<TypeNode>) -> CodamaResult<Self> {
38 Ok(PreOffsetTypeNode {
39 strategy: node.strategy,
40 offset: node.offset,
41 r#type: Box::new(NestedTypeNode::try_from(*node.r#type)?),
42 })
43 }
44}
45
46impl<T: TypeNodeUnionTrait> PreOffsetTypeNode<T> {
47 pub fn new<U>(r#type: U, strategy: PreOffsetStrategy, offset: i32) -> Self
48 where
49 U: Into<T>,
50 {
51 Self {
52 r#type: Box::new(r#type.into()),
53 strategy,
54 offset,
55 }
56 }
57
58 pub fn absolute<U>(r#type: U, offset: i32) -> Self
59 where
60 U: Into<T>,
61 {
62 Self::new(r#type, PreOffsetStrategy::Absolute, offset)
63 }
64
65 pub fn padded<U>(r#type: U, offset: i32) -> Self
66 where
67 U: Into<T>,
68 {
69 Self::new(r#type, PreOffsetStrategy::Padded, offset)
70 }
71
72 pub fn relative<U>(r#type: U, offset: i32) -> Self
73 where
74 U: Into<T>,
75 {
76 Self::new(r#type, PreOffsetStrategy::Relative, offset)
77 }
78}
79
80impl<T: TypeNodeTrait> NestedTypeNodeTrait<T> for PreOffsetTypeNode<NestedTypeNode<T>> {
81 type Mapped<U: TypeNodeTrait> = PreOffsetTypeNode<NestedTypeNode<U>>;
82
83 fn get_nested_type_node(&self) -> &T {
84 self.r#type.get_nested_type_node()
85 }
86
87 fn try_map_nested_type_node<U: TypeNodeTrait, F: FnOnce(T) -> CodamaResult<U>>(
88 self,
89 f: F,
90 ) -> CodamaResult<Self::Mapped<U>> {
91 Ok(PreOffsetTypeNode {
92 r#type: Box::new(self.r#type.try_map_nested_type_node(f)?),
93 strategy: self.strategy,
94 offset: self.offset,
95 })
96 }
97}
98
99#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub enum PreOffsetStrategy {
102 Absolute,
103 Padded,
104 Relative,
105}
106
107#[cfg(test)]
108mod tests {
109 use crate::{NestedTypeNode, NumberTypeNode, StringTypeNode, TypeNode, Utf8, U64};
110
111 use super::*;
112
113 #[test]
114 fn new_type_node() {
115 let node = PreOffsetTypeNode::<TypeNode>::new(
116 StringTypeNode::new(Utf8),
117 PreOffsetStrategy::Absolute,
118 -42,
119 );
120 assert_eq!(*node.r#type, TypeNode::String(StringTypeNode::new(Utf8)));
121 assert_eq!(node.strategy, PreOffsetStrategy::Absolute);
122 assert_eq!(node.offset, -42);
123 }
124
125 #[test]
126 fn new_nested_type_node() {
127 let node = PreOffsetTypeNode::<NestedTypeNode<StringTypeNode>>::new(
128 StringTypeNode::new(Utf8),
129 PreOffsetStrategy::Absolute,
130 -42,
131 );
132 assert_eq!(
133 *node.r#type,
134 NestedTypeNode::Value(StringTypeNode::new(Utf8))
135 );
136 assert_eq!(node.get_nested_type_node(), &StringTypeNode::new(Utf8));
137 assert_eq!(node.strategy, PreOffsetStrategy::Absolute);
138 assert_eq!(node.offset, -42);
139 }
140
141 #[test]
142 fn absolute() {
143 let node = PreOffsetTypeNode::<TypeNode>::absolute(StringTypeNode::new(Utf8), 0);
144 assert_eq!(*node.r#type, TypeNode::String(StringTypeNode::new(Utf8)));
145 assert_eq!(node.strategy, PreOffsetStrategy::Absolute);
146 assert_eq!(node.offset, 0);
147 }
148
149 #[test]
150 fn relative() {
151 let node = PreOffsetTypeNode::<TypeNode>::relative(StringTypeNode::new(Utf8), -4);
152 assert_eq!(*node.r#type, TypeNode::String(StringTypeNode::new(Utf8)));
153 assert_eq!(node.strategy, PreOffsetStrategy::Relative);
154 assert_eq!(node.offset, -4);
155 }
156
157 #[test]
158 fn padded() {
159 let node = PreOffsetTypeNode::<TypeNode>::padded(StringTypeNode::new(Utf8), 8);
160 assert_eq!(*node.r#type, TypeNode::String(StringTypeNode::new(Utf8)));
161 assert_eq!(node.strategy, PreOffsetStrategy::Padded);
162 assert_eq!(node.offset, 8);
163 }
164
165 #[test]
166 fn to_json() {
167 let node = PreOffsetTypeNode::<TypeNode>::padded(NumberTypeNode::le(U64), 4);
168 let json = serde_json::to_string(&node).unwrap();
169 assert_eq!(
170 json,
171 r#"{"kind":"preOffsetTypeNode","offset":4,"strategy":"padded","type":{"kind":"numberTypeNode","format":"u64","endian":"le"}}"#
172 );
173 }
174
175 #[test]
176 fn from_json() {
177 let json = r#"{"kind":"preOffsetTypeNode","offset":4,"strategy":"padded","type":{"kind":"numberTypeNode","format":"u64","endian":"le"}}"#;
178 let node: PreOffsetTypeNode<TypeNode> = serde_json::from_str(json).unwrap();
179 assert_eq!(
180 node,
181 PreOffsetTypeNode::<TypeNode>::padded(NumberTypeNode::le(U64), 4)
182 );
183 }
184}