1use crate::abi;
9
10#[cfg(target_arch = "wasm32")]
13#[link(wasm_import_module = "larvae")]
14unsafe extern "C" {
15 #[link_name = "node_kind"]
16 safe fn host_node_kind(epoch: u64, id: u32) -> i64;
17 #[link_name = "node_text"]
18 safe fn host_node_text(epoch: u64, id: u32) -> i64;
19 #[link_name = "node_span_start"]
20 safe fn host_span_start(epoch: u64, id: u32) -> i64;
21 #[link_name = "node_span_end"]
22 safe fn host_span_end(epoch: u64, id: u32) -> i64;
23 #[link_name = "node_parent"]
24 safe fn host_parent(epoch: u64, id: u32) -> i64;
25 #[link_name = "node_child_count"]
26 safe fn host_child_count(epoch: u64, id: u32) -> i64;
27 #[link_name = "node_child"]
28 safe fn host_child(epoch: u64, id: u32, index: u32) -> i64;
29 #[link_name = "take_str"]
30 safe fn host_take_str(ptr: u32, len: u32) -> i64;
31 #[link_name = "replace"]
32 safe fn host_replace(epoch: u64, id: u32, ptr: u32, len: u32) -> i64;
33 #[link_name = "remove"]
34 safe fn host_remove(epoch: u64, id: u32) -> i64;
35}
36
37#[cfg(not(target_arch = "wasm32"))]
51mod outside_wasm {
52 pub fn host_node_kind(_epoch: u64, _id: u32) -> i64 {
53 absent()
54 }
55
56 pub fn host_node_text(_epoch: u64, _id: u32) -> i64 {
57 absent()
58 }
59
60 pub fn host_span_start(_epoch: u64, _id: u32) -> i64 {
61 absent()
62 }
63
64 pub fn host_span_end(_epoch: u64, _id: u32) -> i64 {
65 absent()
66 }
67
68 pub fn host_parent(_epoch: u64, _id: u32) -> i64 {
69 absent()
70 }
71
72 pub fn host_child_count(_epoch: u64, _id: u32) -> i64 {
73 absent()
74 }
75
76 pub fn host_child(_epoch: u64, _id: u32, _index: u32) -> i64 {
77 absent()
78 }
79
80 pub fn host_take_str(_ptr: u32, _len: u32) -> i64 {
81 absent()
82 }
83
84 pub fn host_replace(_epoch: u64, _id: u32, _ptr: u32, _len: u32) -> i64 {
85 absent()
86 }
87
88 pub fn host_remove(_epoch: u64, _id: u32) -> i64 {
89 absent()
90 }
91
92 fn absent() -> ! {
93 unreachable!("the node API belongs to the wasm form, and this worm is not wasm")
94 }
95}
96
97#[cfg(not(target_arch = "wasm32"))]
98use outside_wasm::*;
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub struct Node {
103 epoch: u64,
104 id: u32,
105}
106
107impl Node {
108 #[doc(hidden)]
110 pub fn from_raw(epoch: u64, id: u32) -> Self {
111 Self { epoch, id }
112 }
113
114 pub fn kind(&self) -> String {
116 pull(host_node_kind(self.epoch, self.id))
117 }
118
119 pub fn text(&self) -> String {
121 pull(host_node_text(self.epoch, self.id))
122 }
123
124 pub fn span(&self) -> (u32, u32) {
126 let start = host_span_start(self.epoch, self.id).max(0) as u32;
127 let end = host_span_end(self.epoch, self.id).max(0) as u32;
128
129 (start, end)
130 }
131
132 pub fn parent(&self) -> Option<Node> {
134 match host_parent(self.epoch, self.id) {
135 id if id < 0 => None,
136
137 id => Some(Node::from_raw(self.epoch, id as u32)),
138 }
139 }
140
141 pub fn children(&self) -> Vec<Node> {
143 let count = host_child_count(self.epoch, self.id).max(0) as u32;
144
145 (0..count)
146 .filter_map(|i| match host_child(self.epoch, self.id, i) {
147 id if id < 0 => None,
148
149 id => Some(Node::from_raw(self.epoch, id as u32)),
150 })
151 .collect()
152 }
153
154 pub fn replace(&self, text: &str) -> bool {
156 host_replace(self.epoch, self.id, text.as_ptr() as u32, text.len() as u32) >= 0
157 }
158
159 pub fn remove(&self) -> bool {
161 host_remove(self.epoch, self.id) >= 0
162 }
163}
164
165fn pull(len: i64) -> String {
172 if len <= 0 {
173 return String::new();
174 }
175
176 let len = len as u32;
177 let buf = abi::alloc(len);
178 let written = host_take_str(buf as u32, len);
179
180 if written < 0 {
181 unsafe { abi::dealloc(buf, len) };
183
184 return String::new();
185 }
186
187 unsafe {
189 let bytes = std::slice::from_raw_parts(buf, written as usize).to_vec();
190 abi::dealloc(buf, len);
191
192 String::from_utf8_unchecked(bytes)
193 }
194}