layer_conform_core/
tree.rs1use compact_str::CompactString;
4
5#[repr(u32)]
6#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
7pub enum NodeKind {
8 Program,
9 FunctionDeclaration,
10 ArrowFunction,
11 Method,
12 CallExpression,
13 MemberExpression,
14 JsxElement,
15 Identifier,
16 Literal,
17 ImportSpecifier,
18 Block,
19 Other,
20}
21
22#[derive(Clone, Debug)]
23pub struct TreeNode {
24 pub kind: NodeKind,
25 pub value: Option<CompactString>,
26 pub children: Vec<Box<TreeNode>>,
27 pub id: u32,
28 pub subtree_size: u32,
29}
30
31impl TreeNode {
32 pub fn leaf(kind: NodeKind, value: Option<CompactString>) -> Self {
34 Self { kind, value, children: Vec::new(), id: 0, subtree_size: 0 }
35 }
36
37 pub fn branch(kind: NodeKind, children: Vec<TreeNode>) -> Self {
39 Self {
40 kind,
41 value: None,
42 children: children.into_iter().map(Box::new).collect(),
43 id: 0,
44 subtree_size: 0,
45 }
46 }
47
48 pub fn finalize(&mut self) {
51 let mut next_id: u32 = 0;
52 Self::finalize_recurse(self, &mut next_id);
53 }
54
55 fn finalize_recurse(node: &mut TreeNode, next_id: &mut u32) {
56 node.id = *next_id;
57 *next_id += 1;
58 let mut size: u32 = 1;
59 for child in &mut node.children {
60 Self::finalize_recurse(child, next_id);
61 size += child.subtree_size;
62 }
63 node.subtree_size = size;
64 }
65
66 pub fn canonical_hash(&self) -> [u8; 32] {
70 let mut hasher = blake3::Hasher::new();
71 Self::write_canonical(self, &mut hasher);
72 *hasher.finalize().as_bytes()
73 }
74
75 fn write_canonical(node: &TreeNode, hasher: &mut blake3::Hasher) {
76 hasher.update(&(node.kind as u32).to_le_bytes());
77 let v = node.value.as_deref().unwrap_or("");
78 let v_bytes = v.as_bytes();
79 hasher.update(&(v_bytes.len() as u32).to_le_bytes());
80 hasher.update(v_bytes);
81 hasher.update(&(node.children.len() as u32).to_le_bytes());
82 for child in &node.children {
83 Self::write_canonical(child, hasher);
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn node_kind_discriminants_are_stable() {
94 assert_eq!(NodeKind::Program as u32, 0);
97 assert_eq!(NodeKind::FunctionDeclaration as u32, 1);
98 assert_eq!(NodeKind::ArrowFunction as u32, 2);
99 }
100
101 #[test]
102 fn node_kind_is_copy_and_eq() {
103 let a = NodeKind::Identifier;
104 let b = a;
105 assert_eq!(a, b);
106 }
107
108 #[test]
109 fn leaf_constructor_has_no_children() {
110 let n = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
111 assert_eq!(n.kind, NodeKind::Identifier);
112 assert_eq!(n.value.as_deref(), Some("x"));
113 assert_eq!(n.children.len(), 0);
114 }
115
116 #[test]
117 fn branch_constructor_owns_children() {
118 let leaf = TreeNode::leaf(NodeKind::Identifier, None);
119 let branch = TreeNode::branch(NodeKind::Block, vec![leaf]);
120 assert_eq!(branch.children.len(), 1);
121 assert_eq!(branch.children[0].kind, NodeKind::Identifier);
122 }
123
124 #[test]
125 fn finalize_assigns_preorder_ids() {
126 let leaf1 = TreeNode::leaf(NodeKind::Identifier, Some("a".into()));
131 let leaf2 = TreeNode::leaf(NodeKind::Identifier, Some("b".into()));
132 let mut root = TreeNode::branch(NodeKind::Block, vec![leaf1, leaf2]);
133 root.finalize();
134 assert_eq!(root.id, 0);
135 assert_eq!(root.subtree_size, 3);
136 assert_eq!(root.children[0].id, 1);
137 assert_eq!(root.children[0].subtree_size, 1);
138 assert_eq!(root.children[1].id, 2);
139 assert_eq!(root.children[1].subtree_size, 1);
140 }
141
142 #[test]
143 fn finalize_handles_nested_subtrees() {
144 let leaf1 = TreeNode::leaf(NodeKind::Identifier, None);
150 let leaf2 = TreeNode::leaf(NodeKind::Identifier, None);
151 let inner = TreeNode::branch(NodeKind::Block, vec![leaf1, leaf2]);
152 let mut root = TreeNode::branch(NodeKind::Block, vec![inner]);
153 root.finalize();
154 assert_eq!(root.subtree_size, 4);
155 assert_eq!(root.children[0].subtree_size, 3);
156 }
157
158 #[test]
159 fn canonical_hash_is_deterministic() {
160 let mut tree = TreeNode::branch(
161 NodeKind::Block,
162 vec![TreeNode::leaf(NodeKind::Identifier, Some("x".into()))],
163 );
164 tree.finalize();
165 let h1 = tree.canonical_hash();
166 let h2 = tree.canonical_hash();
167 assert_eq!(h1, h2);
168 }
169
170 #[test]
171 fn canonical_hash_differs_for_different_kinds() {
172 let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
173 t1.finalize();
174 let mut t2 = TreeNode::leaf(NodeKind::Literal, Some("x".into()));
175 t2.finalize();
176 assert_ne!(t1.canonical_hash(), t2.canonical_hash());
177 }
178
179 #[test]
180 fn canonical_hash_differs_for_different_values() {
181 let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
182 t1.finalize();
183 let mut t2 = TreeNode::leaf(NodeKind::Identifier, Some("y".into()));
184 t2.finalize();
185 assert_ne!(t1.canonical_hash(), t2.canonical_hash());
186 }
187
188 #[test]
189 fn canonical_hash_ignores_id_and_size() {
190 let mut t1 = TreeNode::leaf(NodeKind::Identifier, Some("x".into()));
192 t1.id = 100;
193 t1.subtree_size = 999;
194 let h1 = t1.canonical_hash();
195 t1.id = 0;
196 t1.subtree_size = 1;
197 let h2 = t1.canonical_hash();
198 assert_eq!(h1, h2);
199 }
200}