waggle_lens_code/
outline.rs1use serde::{Deserialize, Serialize};
7
8pub const WIRE_VERSION: u16 = 1;
11
12pub(crate) const ROOT: u32 = u32::MAX;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub(crate) struct Sym {
20 pub name: (u32, u16),
21 pub kind: u8,
22 pub start_line: u32,
23 pub end_line: u32,
24 pub parent: u32,
25 pub depth: u8,
26}
27
28#[derive(Debug, Default, Clone, PartialEq, Eq)]
31pub struct SymbolOutline {
32 pub(crate) syms: Vec<Sym>,
33 pub(crate) names: String,
34 pub(crate) kinds: Vec<String>,
37}
38
39impl SymbolOutline {
40 #[must_use]
42 pub fn len(&self) -> usize {
43 self.syms.len()
44 }
45
46 #[must_use]
48 pub fn is_empty(&self) -> bool {
49 self.syms.is_empty()
50 }
51
52 pub(crate) fn name_of(&self, s: &Sym) -> &str {
53 let (off, len) = s.name;
54 &self.names[off as usize..off as usize + len as usize]
55 }
56
57 #[must_use]
60 pub fn find(&self, name: &str) -> Vec<usize> {
61 self.syms
62 .iter()
63 .enumerate()
64 .filter(|(_, s)| self.name_of(s) == name)
65 .map(|(i, _)| i)
66 .collect()
67 }
68
69 #[must_use]
71 pub fn name_at(&self, idx: usize) -> Option<&str> {
72 self.syms.get(idx).map(|s| self.name_of(s))
73 }
74
75 #[must_use]
77 pub fn lines_of(&self, idx: usize) -> Option<(u32, u32, &str)> {
78 let s = self.syms.get(idx)?;
79 Some((
80 s.start_line,
81 s.end_line,
82 self.kinds.get(s.kind as usize).map_or("", String::as_str),
83 ))
84 }
85
86 #[must_use]
92 pub fn to_wire(&self) -> Vec<u8> {
93 let w = Wire {
94 x: WIRE_VERSION,
95 kinds: self.kinds.clone(),
96 names: self
97 .syms
98 .iter()
99 .map(|s| self.name_of(s).to_owned())
100 .collect(),
101 kind: self.syms.iter().map(|s| s.kind).collect(),
102 start: self.syms.iter().map(|s| s.start_line).collect(),
103 end: self.syms.iter().map(|s| s.end_line).collect(),
104 depth: self.syms.iter().map(|s| s.depth).collect(),
105 };
106 serde_json::to_vec(&w).expect("plain data always serializes")
107 }
108}
109
110#[derive(Serialize, Deserialize)]
116struct Wire {
117 x: u16,
118 kinds: Vec<String>,
119 names: Vec<String>,
120 kind: Vec<u8>,
121 start: Vec<u32>,
122 end: Vec<u32>,
123 depth: Vec<u8>,
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 fn outline(n: usize) -> SymbolOutline {
131 let mut o = SymbolOutline {
132 kinds: vec!["function".into(), "class".into()],
133 ..SymbolOutline::default()
134 };
135 for i in 0..n {
136 let name = format!("sym_{i}");
137 let off = u32::try_from(o.names.len()).unwrap();
138 o.names.push_str(&name);
139 o.syms.push(Sym {
140 name: (off, u16::try_from(name.len()).unwrap()),
141 kind: u8::try_from(i % 2).unwrap(),
142 start_line: u32::try_from(i * 10 + 1).unwrap(),
143 end_line: u32::try_from(i * 10 + 8).unwrap(),
144 parent: ROOT,
145 depth: u8::try_from(i % 3).unwrap(),
146 });
147 }
148 o
149 }
150
151 #[test]
152 fn wire_is_parallel_arrays_with_the_version_pin() {
153 let o = outline(7);
154 let v: serde_json::Value = serde_json::from_slice(&o.to_wire()).unwrap();
155 assert_eq!(v["x"], WIRE_VERSION);
156 for field in ["names", "kind", "start", "end", "depth"] {
157 assert_eq!(v[field].as_array().unwrap().len(), 7, "{field}");
158 }
159 assert_eq!(v["names"][3], "sym_3");
160 assert_eq!(v["start"][3], 31);
161 assert_eq!(v["kinds"][0], "function");
162 }
163
164 #[test]
165 fn arena_lookups() {
166 let o = outline(7);
167 assert_eq!(o.find("sym_3"), vec![3]);
168 let (start, end, kind) = o.lines_of(3).unwrap();
169 assert_eq!((start, end), (31, 38));
170 assert_eq!(kind, "class");
171 assert!(o.find("nope").is_empty());
172 }
173}