1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::fmt;
6use std::hash::DefaultHasher;
7use std::hash::Hash;
8use std::hash::Hasher;
9
10use itertools::Itertools;
11use wgsl_parse::syntax::Expression;
12use wgsl_parse::syntax::PathOrigin;
13use wgsl_parse::syntax::TypeExpression;
14
15use super::ModulePath;
16
17pub trait Mangler {
32 fn mangle(&self, path: &ModulePath, item: &str) -> String;
34 fn unmangle(&self, _mangled: &str) -> Option<(ModulePath, String)> {
36 None
37 }
38 fn mangle_types(&self, item: &str, variant: u32, _types: &[TypeExpression]) -> String {
40 format!("{item}_{variant}")
41 }
42}
43
44impl<T: Mangler + ?Sized> Mangler for Box<T> {
45 fn mangle(&self, path: &ModulePath, item: &str) -> String {
46 (**self).mangle(path, item)
47 }
48 fn unmangle(&self, mangled: &str) -> Option<(ModulePath, String)> {
49 (**self).unmangle(mangled)
50 }
51 fn mangle_types(&self, item: &str, variant: u32, types: &[TypeExpression]) -> String {
52 (**self).mangle_types(item, variant, types)
53 }
54}
55
56impl<T: Mangler + ?Sized> Mangler for &T {
57 fn mangle(&self, path: &ModulePath, item: &str) -> String {
58 (**self).mangle(path, item)
59 }
60 fn unmangle(&self, mangled: &str) -> Option<(ModulePath, String)> {
61 (**self).unmangle(mangled)
62 }
63 fn mangle_types(&self, item: &str, variant: u32, types: &[TypeExpression]) -> String {
64 (**self).mangle_types(item, variant, types)
65 }
66}
67
68#[derive(Default, Clone, Debug)]
71pub struct HashMangler;
72
73impl Mangler for HashMangler {
74 fn mangle(&self, path: &ModulePath, item: &str) -> String {
75 let mut hasher = DefaultHasher::new();
76 path.hash(&mut hasher);
77 item.hash(&mut hasher);
78 let hash = hasher.finish();
79 format!("{item}_{hash}")
80 }
81}
82
83#[derive(Default, Clone, Debug)]
92pub struct EscapeMangler;
93
94impl EscapeMangler {
95 fn escape_component(comp: &str) -> String {
96 if comp.contains('/') {
97 let count = comp.chars().filter(|c| *c == '/').count() + 1;
99 format!(
100 "import_{count}_{}",
101 comp.split('/').map(Self::escape_component).format("_")
102 )
103 } else {
104 let underscores = comp.chars().filter(|c| *c == '_').count();
105 if underscores > 0 {
106 format!("_{underscores}{comp}")
107 } else {
108 comp.to_string()
109 }
110 }
111 }
112
113 fn unescape_component<'a>(
114 part: &str,
115 parts: &mut impl Iterator<Item = &'a str>,
116 ) -> Option<String> {
117 fn extract_count(part: &str) -> Option<(usize, &str)> {
118 let digits = part.chars().take_while(|c| c.is_ascii_digit()).count();
119
120 if digits == 0 {
121 return None;
122 }
123
124 let count = part[..digits].parse().ok()?;
125 Some((count, &part[digits..]))
126 }
127
128 let component = if part.is_empty() {
129 let first = parts.next()?;
130 let (n, rem) = extract_count(first)?;
131
132 std::iter::once(Some(rem))
133 .chain((0..n).map(|_| parts.next()))
134 .collect::<Option<Vec<_>>>()?
135 .join("_")
136 } else {
137 part.to_string()
138 };
139
140 Some(component)
141 }
142}
143
144impl Mangler for EscapeMangler {
145 fn mangle(&self, path: &ModulePath, item: &str) -> String {
146 let origin = match &path.origin {
147 PathOrigin::Absolute => "package".to_string(),
148 PathOrigin::Relative(0) => "self".to_string(),
149 PathOrigin::Relative(n) => format!("{}", (0..*n).map(|_| "super").format("_")),
150 PathOrigin::Package(name) => Self::escape_component(name).to_string(),
151 };
152
153 let item = Self::escape_component(item);
154
155 if path.components.is_empty() {
156 format!("{origin}_{item}",)
157 } else {
158 let path = path
159 .components
160 .iter()
161 .map(|comp| Self::escape_component(comp))
162 .format("_");
163
164 format!("{origin}_{path}_{item}",)
165 }
166 }
167
168 fn unmangle(&self, mangled: &str) -> Option<(ModulePath, String)> {
171 let mut components = Vec::new();
172 let mut parts = mangled.split('_').peekable();
173
174 let origin = match parts.next() {
175 Some("package") => PathOrigin::Absolute,
176 Some("self") => PathOrigin::Relative(0),
177 Some("super") => {
178 let mut n = 1;
179 while let Some(&"super") = parts.peek() {
180 n += 1;
181 parts.next().unwrap();
182 }
183 PathOrigin::Relative(n)
184 }
185 Some("import") => {
186 let count: u32 = parts.next()?.parse().ok()?;
187 let pkg_name = (0..count)
188 .map(|_| Self::unescape_component(parts.next()?, &mut parts))
189 .collect::<Option<Vec<_>>>()?
190 .join("/");
191 PathOrigin::Package(pkg_name)
192 }
193 Some(name) => PathOrigin::Package(Self::unescape_component(name, &mut parts)?),
194 None => return None,
195 };
196
197 while let Some(part) = parts.next() {
198 components.push(Self::unescape_component(part, &mut parts)?);
199 }
200
201 let item = components.pop()?;
202
203 let path = ModulePath::new(origin, components);
204 Some((path, item))
205 }
206}
207
208#[test]
209fn test_escape_mangler() {
210 let tests = [
211 (
212 "package::bevy_pbr::lighting",
213 "package__1bevy_pbr_lighting_item",
214 ),
215 ("a_b::c_d::e", "_1a_b__1c_d_e_item"),
216 ("self::a", "self_a_item"),
217 ("super::super", "super_super_item"),
218 ("package::_a_b__c___", "package__7_a_b__c____item"),
219 ("bevy/pbr", "import_2_bevy_pbr_item"),
221 ("bevy/pbr::lighting", "import_2_bevy_pbr_lighting_item"),
222 ("bevy_pbr::lighting", "_1bevy_pbr_lighting_item"),
223 ];
224
225 for (p, m) in tests {
226 let p = p.parse().expect("failed to parse module path");
227 println!("testing {p}::item -> {m}");
228 assert_eq!(EscapeMangler.mangle(&p, "item"), m);
229 assert_eq!(EscapeMangler.unmangle(m), Some((p, "item".to_string())));
230 }
231}
232
233#[derive(Default, Clone, Debug)]
238pub struct NoMangler;
239
240impl Mangler for NoMangler {
241 fn mangle(&self, _path: &ModulePath, item: &str) -> String {
242 item.to_string()
243 }
244}
245
246pub struct CacheMangler<'a, T: Mangler> {
248 cache: RefCell<HashMap<String, (ModulePath, String)>>,
249 mangler: &'a T,
250}
251
252impl<'a, T: Mangler> CacheMangler<'a, T> {
253 pub fn new(mangler: &'a T) -> Self {
254 Self {
255 cache: Default::default(),
256 mangler,
257 }
258 }
259}
260
261impl<T: Mangler> Mangler for CacheMangler<'_, T> {
262 fn mangle(&self, path: &ModulePath, item: &str) -> String {
263 let res = self.mangler.mangle(path, item);
264 let mut cache = self.cache.borrow_mut();
265 cache.insert(res.clone(), (path.clone(), item.to_string()));
266 res
267 }
268 fn unmangle(&self, mangled: &str) -> Option<(ModulePath, String)> {
269 {
270 let cache = self.cache.borrow();
271 if let Some(res) = cache.get(mangled).cloned() {
272 return Some(res);
273 }
274 }
275
276 self.mangler.unmangle(mangled)
277 }
278}
279
280#[derive(Default, Clone, Debug)]
288pub struct UnicodeMangler;
289
290impl UnicodeMangler {
291 const LT: char = 'ᐸ'; const GT: char = 'ᐳ'; const SEP: &'static str = "::"; const TY_SEP: &'static str = "ˏ"; fn display_ty(ty: &TypeExpression) -> impl fmt::Display + '_ {
297 format_args!(
298 "{}{}",
299 ty.ident,
300 ty.template_args
301 .iter()
302 .format_with(Self::TY_SEP, |tplt, f| {
303 f(&format_args!(
304 "{}{}{}",
305 Self::LT,
306 tplt.iter().format_with(Self::TY_SEP, |tplt, f| {
307 match tplt.expression.node() {
308 Expression::Literal(lit) => f(lit),
309 Expression::TypeOrIdentifier(ty) => f(&Self::display_ty(ty)),
310 _ => panic!("only type names can be mangled"),
311 }
312 }),
313 Self::GT
314 ))
315 })
316 )
317 .to_string()
318 }
319}
320
321impl Mangler for UnicodeMangler {
322 fn mangle(&self, path: &ModulePath, item: &str) -> String {
323 let sep = Self::SEP;
324 let origin = match &path.origin {
325 PathOrigin::Absolute => format!("package{sep}"),
326 PathOrigin::Relative(0) => format!("self{sep}"),
327 PathOrigin::Relative(n) => format!("{}{sep}", (1..*n).map(|_| "super").format(sep)),
328 PathOrigin::Package(name) => format!("{name}{sep}"),
329 };
330 let path = path.components.iter().format(sep);
331 format!("{origin}{path}{sep}{item}")
332 }
333
334 fn unmangle(&self, mangled: &str) -> Option<(ModulePath, String)> {
335 let mut components = mangled.split(Self::SEP).map(str::to_string).collect_vec();
336 let mut origin = PathOrigin::Absolute;
337 while let Some(comp) = components.first() {
338 match comp.as_str() {
339 "package" => {
340 components.remove(0);
341 origin = PathOrigin::Absolute;
342 break;
343 }
344 "self" => {
345 components.remove(0);
346 origin = PathOrigin::Relative(0);
347 break;
348 }
349 "super" => {
350 components.remove(0);
351 origin = match origin {
352 PathOrigin::Relative(n) => PathOrigin::Relative(n + 1),
353 _ => PathOrigin::Relative(1),
354 };
355 }
356 _ => break,
357 }
358 }
359 let name = components.pop()?;
360 Some((ModulePath::new(origin, components), name))
361 }
362 fn mangle_types(&self, item: &str, _variant: u32, types: &[TypeExpression]) -> String {
363 format!(
365 "{item}{}{}{}",
366 Self::LT,
367 types
368 .iter()
369 .format_with(Self::TY_SEP, |ty, f| f(&Self::display_ty(ty))),
370 Self::GT
371 )
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378
379 #[test]
380 fn mangle_textures3d() {
381 let mangler = EscapeMangler;
382 let module_path = ModulePath::new_root();
383 let mangled = mangler.mangle(&module_path, "textures_3d");
384 assert_eq!("package__1textures_3d", mangled);
385 }
386
387 #[test]
388 fn unmangle_textures3d() {
389 let mangler = EscapeMangler;
390 let unmangled = mangler.unmangle("package__1textures_3d").map(|x| x.1);
391 assert_eq!(Some("textures_3d"), unmangled.as_deref());
392 }
393}