1use crate::abi;
4
5#[link(wasm_import_module = "larvae")]
6unsafe extern "C" {
7 #[link_name = "node_kind"]
8 safe fn host_node_kind(epoch: u64, id: u32) -> i64;
9 #[link_name = "node_text"]
10 safe fn host_node_text(epoch: u64, id: u32) -> i64;
11 #[link_name = "node_span_start"]
12 safe fn host_span_start(epoch: u64, id: u32) -> i64;
13 #[link_name = "node_span_end"]
14 safe fn host_span_end(epoch: u64, id: u32) -> i64;
15 #[link_name = "node_parent"]
16 safe fn host_parent(epoch: u64, id: u32) -> i64;
17 #[link_name = "node_child_count"]
18 safe fn host_child_count(epoch: u64, id: u32) -> i64;
19 #[link_name = "node_child"]
20 safe fn host_child(epoch: u64, id: u32, index: u32) -> i64;
21 #[link_name = "take_str"]
22 safe fn host_take_str(ptr: u32, len: u32) -> i64;
23 #[link_name = "replace"]
24 safe fn host_replace(epoch: u64, id: u32, ptr: u32, len: u32) -> i64;
25 #[link_name = "remove"]
26 safe fn host_remove(epoch: u64, id: u32) -> i64;
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct Node {
32 epoch: u64,
33 id: u32,
34}
35
36impl Node {
37 #[doc(hidden)]
39 pub fn from_raw(epoch: u64, id: u32) -> Self {
40 Self { epoch, id }
41 }
42
43 pub fn kind(&self) -> String {
45 pull(host_node_kind(self.epoch, self.id))
46 }
47
48 pub fn text(&self) -> String {
50 pull(host_node_text(self.epoch, self.id))
51 }
52
53 pub fn span(&self) -> (u32, u32) {
55 let start = host_span_start(self.epoch, self.id).max(0) as u32;
56 let end = host_span_end(self.epoch, self.id).max(0) as u32;
57
58 (start, end)
59 }
60
61 pub fn parent(&self) -> Option<Node> {
63 match host_parent(self.epoch, self.id) {
64 id if id < 0 => None,
65
66 id => Some(Node::from_raw(self.epoch, id as u32)),
67 }
68 }
69
70 pub fn children(&self) -> Vec<Node> {
72 let count = host_child_count(self.epoch, self.id).max(0) as u32;
73
74 (0..count)
75 .filter_map(|i| match host_child(self.epoch, self.id, i) {
76 id if id < 0 => None,
77
78 id => Some(Node::from_raw(self.epoch, id as u32)),
79 })
80 .collect()
81 }
82
83 pub fn replace(&self, text: &str) -> bool {
85 host_replace(self.epoch, self.id, text.as_ptr() as u32, text.len() as u32) >= 0
86 }
87
88 pub fn remove(&self) -> bool {
90 host_remove(self.epoch, self.id) >= 0
91 }
92}
93
94fn pull(len: i64) -> String {
101 if len <= 0 {
102 return String::new();
103 }
104
105 let len = len as u32;
106 let buf = abi::alloc(len);
107 let written = host_take_str(buf as u32, len);
108
109 if written < 0 {
110 unsafe { abi::dealloc(buf, len) };
112
113 return String::new();
114 }
115
116 unsafe {
118 let bytes = std::slice::from_raw_parts(buf, written as usize).to_vec();
119 abi::dealloc(buf, len);
120
121 String::from_utf8_unchecked(bytes)
122 }
123}