panproto_parse/
id_scheme.rs1#[derive(Debug)]
20pub struct IdGenerator {
21 scope_stack: Vec<String>,
23 child_counter: Vec<u32>,
25}
26
27impl IdGenerator {
28 #[must_use]
30 pub fn new(file_path: &str) -> Self {
31 Self {
32 scope_stack: vec![file_path.to_owned()],
33 child_counter: vec![0],
34 }
35 }
36
37 pub fn push_named_scope(&mut self, name: &str) {
42 self.scope_stack.push(name.to_owned());
43 self.child_counter.push(0);
44 }
45
46 pub fn push_anonymous_scope(&mut self) -> u32 {
51 let idx = self.child_counter.last().copied().unwrap_or(0);
52 if let Some(counter) = self.child_counter.last_mut() {
53 *counter += 1;
54 }
55
56 self.scope_stack.push(format!("${idx}"));
57 self.child_counter.push(0);
58 idx
59 }
60
61 pub fn pop_scope(&mut self) {
63 if self.scope_stack.len() > 1 {
64 self.scope_stack.pop();
65 self.child_counter.pop();
66 }
67 }
68
69 #[must_use]
73 pub fn named_id(&self, name: &str) -> String {
74 if self.scope_stack.len() == 1 {
75 format!("{}::{name}", self.scope_stack[0])
76 } else {
77 format!("{}::{name}", self.current_prefix())
78 }
79 }
80
81 pub fn anonymous_id(&mut self) -> String {
86 let idx = self.child_counter.last().copied().unwrap_or(0);
87 if let Some(counter) = self.child_counter.last_mut() {
88 *counter += 1;
89 }
90
91 format!("{}::${idx}", self.current_prefix())
92 }
93
94 #[must_use]
99 pub fn field_id(&self, base_id: &str, field_name: &str) -> String {
100 format!("{base_id}.{field_name}")
101 }
102
103 #[must_use]
105 pub fn current_prefix(&self) -> String {
106 self.scope_stack.join("::")
107 }
108
109 #[must_use]
111 pub fn depth(&self) -> usize {
112 self.scope_stack.len()
113 }
114
115 pub fn reset_counter(&mut self) {
119 if let Some(counter) = self.child_counter.last_mut() {
120 *counter = 0;
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn basic_named_ids() {
131 let id_gen = IdGenerator::new("src/main.rs");
132 assert_eq!(id_gen.named_id("User"), "src/main.rs::User");
133 }
134
135 #[test]
136 fn nested_scopes() {
137 let mut id_gen = IdGenerator::new("src/lib.rs");
138 id_gen.push_named_scope("Parser");
139 id_gen.push_named_scope("parse");
140 assert_eq!(
141 id_gen.named_id("config"),
142 "src/lib.rs::Parser::parse::config"
143 );
144 id_gen.pop_scope();
145 assert_eq!(id_gen.named_id("new"), "src/lib.rs::Parser::new");
146 }
147
148 #[test]
149 fn anonymous_ids_increment() {
150 let mut id_gen = IdGenerator::new("test.ts");
151 id_gen.push_named_scope("main");
152
153 let id0 = id_gen.anonymous_id();
154 let id1 = id_gen.anonymous_id();
155 let id2 = id_gen.anonymous_id();
156
157 assert_eq!(id0, "test.ts::main::$0");
158 assert_eq!(id1, "test.ts::main::$1");
159 assert_eq!(id2, "test.ts::main::$2");
160 }
161
162 #[test]
163 fn anonymous_scopes() {
164 let mut id_gen = IdGenerator::new("test.py");
165 id_gen.push_named_scope("process");
166 let _stmt_idx = id_gen.push_anonymous_scope(); let inner = id_gen.anonymous_id();
168 assert_eq!(inner, "test.py::process::$0::$0");
169 id_gen.pop_scope();
170 let _stmt_idx2 = id_gen.push_anonymous_scope(); let inner2 = id_gen.anonymous_id();
172 assert_eq!(inner2, "test.py::process::$1::$0");
173 }
174
175 #[test]
176 fn field_ids() {
177 let id_gen = IdGenerator::new("test.rs");
178 let base = id_gen.named_id("expr");
179 let left = id_gen.field_id(&base, "left");
180 let right = id_gen.field_id(&base, "right");
181 assert_eq!(left, "test.rs::expr.left");
182 assert_eq!(right, "test.rs::expr.right");
183 }
184
185 #[test]
186 fn depth_tracking() {
187 let mut id_gen = IdGenerator::new("f.ts");
188 assert_eq!(id_gen.depth(), 1);
189 id_gen.push_named_scope("fn");
190 assert_eq!(id_gen.depth(), 2);
191 id_gen.push_anonymous_scope();
192 assert_eq!(id_gen.depth(), 3);
193 id_gen.pop_scope();
194 assert_eq!(id_gen.depth(), 2);
195 }
196}