1use crate::rust_v0::{
4 Abi, BasicType, Const, ConstFields, DynBounds, DynTrait, DynTraitAssocBinding, FnSig, GenericArg, Path, Type,
5};
6use std::any;
7use std::cell::Cell;
8use std::fmt::{self, Display, Formatter, Write};
9
10#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub enum Style {
13 Short,
15 Normal,
17 Long,
20}
21
22fn display_fn(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
23 struct Wrapper<F>(F);
24
25 impl<F: Fn(&mut Formatter) -> fmt::Result> Display for Wrapper<F> {
26 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
27 self.0(f)
28 }
29 }
30
31 Wrapper(f)
32}
33
34fn display_separated_list(values: impl IntoIterator<Item = impl Display>, separator: impl Display) -> impl Display {
35 let values = Cell::new(Some(values));
36
37 display_fn(move |f| {
38 let mut iter = values.take().unwrap().into_iter();
39
40 if let Some(first) = iter.next() {
41 first.fmt(f)?;
42
43 for value in iter {
44 separator.fmt(f)?;
45 value.fmt(f)?;
46 }
47 }
48
49 Ok(())
50 })
51}
52
53pub fn display_path<'a>(path: &'a Path, style: Style, bound_lifetime_depth: u64, in_value: bool) -> impl Display + 'a {
54 display_fn(move |f| match path {
55 Path::CrateRoot(identifier) => match style {
56 Style::Short | Style::Normal => f.write_str(&identifier.name),
57 Style::Long => {
58 write!(f, "{}[{:x}]", identifier.name, identifier.disambiguator)
59 }
60 },
61 Path::InherentImpl { type_, .. } => {
62 write!(f, "<{}>", display_type(type_, style, bound_lifetime_depth))
63 }
64 Path::TraitImpl { type_, trait_, .. } | Path::TraitDefinition { type_, trait_ } => {
65 write!(
66 f,
67 "<{} as {}>",
68 display_type(type_, style, bound_lifetime_depth),
69 display_path(trait_, style, bound_lifetime_depth, false)
70 )
71 }
72 Path::Nested {
73 namespace,
74 path,
75 identifier,
76 } => match namespace {
77 b'A'..=b'Z' => {
78 display_path(path, style, bound_lifetime_depth, in_value).fmt(f)?;
79
80 f.write_str("::{")?;
81
82 match namespace {
83 b'C' => f.write_str("closure")?,
84 b'S' => f.write_str("shim")?,
85 _ => f.write_char(char::from(*namespace))?,
86 }
87
88 if !identifier.name.is_empty() {
89 write!(f, ":{}", identifier.name)?;
90 }
91
92 write!(f, "#{}}}", identifier.disambiguator)
93 }
94 b'a'..=b'z' => {
95 if matches!(style, Style::Normal | Style::Long)
96 || matches!(
97 path.as_ref(),
98 Path::InherentImpl { .. }
99 | Path::TraitImpl { .. }
100 | Path::TraitDefinition { .. }
101 | Path::Generic { .. }
102 )
103 {
104 display_path(path, style, bound_lifetime_depth, in_value).fmt(f)?;
105
106 if identifier.name.is_empty() {
107 Ok(())
108 } else {
109 write!(f, "::{}", identifier.name)
110 }
111 } else if identifier.name.is_empty() {
112 display_path(path, style, bound_lifetime_depth, in_value).fmt(f)
113 } else {
114 write!(f, "{}", identifier.name)
115 }
116 }
117 _ => Err(fmt::Error),
118 },
119 Path::Generic { path, generic_args } => {
120 display_path(path, style, bound_lifetime_depth, in_value).fmt(f)?;
121
122 if in_value {
123 f.write_str("::")?;
124 }
125
126 write!(
127 f,
128 "<{}>",
129 display_separated_list(
130 generic_args.iter().map(|generic_arg| display_generic_arg(
131 generic_arg,
132 style,
133 bound_lifetime_depth,
134 )),
135 ", "
136 )
137 )
138 }
139 })
140}
141
142fn display_lifetime(lifetime: u64, bound_lifetime_depth: u64) -> impl Display {
143 display_fn(move |f| {
144 f.write_char('\'')?;
145
146 if lifetime == 0 {
147 f.write_char('_')
148 } else if let Some(depth) = bound_lifetime_depth.checked_sub(lifetime) {
149 if depth < 26 {
150 f.write_char(char::from(b'a' + u8::try_from(depth).unwrap()))
151 } else {
152 write!(f, "_{}", depth)
153 }
154 } else {
155 Err(fmt::Error)
156 }
157 })
158}
159
160pub fn display_generic_arg<'a>(
161 generic_arg: &'a GenericArg,
162 style: Style,
163 bound_lifetime_depth: u64,
164) -> impl Display + 'a {
165 display_fn(move |f| match generic_arg {
166 GenericArg::Lifetime(lifetime) => display_lifetime(*lifetime, bound_lifetime_depth).fmt(f),
167 GenericArg::Type(type_) => display_type(type_, style, bound_lifetime_depth).fmt(f),
168 GenericArg::Const(const_) => display_const(const_, style, bound_lifetime_depth, false).fmt(f),
169 })
170}
171
172fn display_binder(bound_lifetimes: u64, bound_lifetime_depth: u64) -> impl Display {
173 display_fn(move |f| {
174 write!(
175 f,
176 "for<{}>",
177 display_separated_list(
178 (1..=bound_lifetimes)
179 .rev()
180 .map(|i| display_lifetime(i, bound_lifetime_depth + bound_lifetimes)),
181 ", "
182 )
183 )
184 })
185}
186
187pub fn display_type<'a>(type_: &'a Type, style: Style, bound_lifetime_depth: u64) -> impl Display + 'a {
188 display_fn(move |f| match type_ {
189 Type::Basic(basic_type) => display_basic_type(*basic_type).fmt(f),
190 Type::Named(path) => display_path(path, style, bound_lifetime_depth, false).fmt(f),
191 Type::Array(type_, length) => {
192 write!(
193 f,
194 "[{}; {}]",
195 display_type(type_, style, bound_lifetime_depth),
196 display_const(length, style, bound_lifetime_depth, true)
197 )
198 }
199 Type::Slice(type_) => {
200 write!(f, "[{}]", display_type(type_, style, bound_lifetime_depth))
201 }
202 Type::Tuple(tuple_types) => {
203 write!(
204 f,
205 "({}",
206 display_separated_list(
207 tuple_types
208 .iter()
209 .map(|type_| { display_type(type_, style, bound_lifetime_depth) }),
210 ", "
211 )
212 )?;
213
214 if tuple_types.len() == 1 {
215 f.write_char(',')?;
216 }
217
218 f.write_char(')')
219 }
220 Type::Ref { lifetime, type_ } => {
221 f.write_char('&')?;
222
223 if *lifetime != 0 {
224 write!(f, "{} ", display_lifetime(*lifetime, bound_lifetime_depth))?;
225 }
226
227 display_type(type_, style, bound_lifetime_depth).fmt(f)
228 }
229 Type::RefMut { lifetime, type_ } => {
230 f.write_char('&')?;
231
232 if *lifetime != 0 {
233 write!(f, "{} ", display_lifetime(*lifetime, bound_lifetime_depth))?;
234 }
235
236 write!(f, "mut {}", display_type(type_, style, bound_lifetime_depth))
237 }
238 Type::PtrConst(type_) => {
239 write!(f, "*const {}", display_type(type_, style, bound_lifetime_depth))
240 }
241 Type::PtrMut(type_) => {
242 write!(f, "*mut {}", display_type(type_, style, bound_lifetime_depth))
243 }
244 Type::Fn(fn_sig) => display_fn_sig(fn_sig, style, bound_lifetime_depth).fmt(f),
245 Type::DynTrait { dyn_bounds, lifetime } => {
246 display_dyn_bounds(dyn_bounds, style, bound_lifetime_depth).fmt(f)?;
247
248 if *lifetime == 0 {
249 Ok(())
250 } else {
251 write!(f, " + {}", display_lifetime(*lifetime, bound_lifetime_depth))
252 }
253 }
254 })
255}
256
257pub fn display_basic_type(basic_type: BasicType) -> impl Display {
258 display_fn(move |f| {
259 f.write_str(match basic_type {
260 BasicType::I8 => "i8",
261 BasicType::Bool => "bool",
262 BasicType::Char => "char",
263 BasicType::F64 => "f64",
264 BasicType::Str => "str",
265 BasicType::F32 => "f32",
266 BasicType::U8 => "u8",
267 BasicType::Isize => "isize",
268 BasicType::Usize => "usize",
269 BasicType::I32 => "i32",
270 BasicType::U32 => "u32",
271 BasicType::I128 => "i128",
272 BasicType::U128 => "u128",
273 BasicType::I16 => "i16",
274 BasicType::U16 => "u16",
275 BasicType::Unit => "()",
276 BasicType::Ellipsis => "...",
277 BasicType::I64 => "i64",
278 BasicType::U64 => "u64",
279 BasicType::Never => "!",
280 BasicType::Placeholder => "_",
281 })
282 })
283}
284
285pub fn display_fn_sig<'a>(fn_sig: &'a FnSig, style: Style, bound_lifetime_depth: u64) -> impl Display + 'a {
286 display_fn(move |f| {
287 if fn_sig.bound_lifetimes != 0 {
288 write!(f, "{} ", display_binder(fn_sig.bound_lifetimes, bound_lifetime_depth))?;
289 }
290
291 let bound_lifetime_depth = bound_lifetime_depth + fn_sig.bound_lifetimes;
292
293 if fn_sig.is_unsafe {
294 f.write_str("unsafe ")?;
295 }
296
297 if let Some(abi) = &fn_sig.abi {
298 write!(f, "extern {} ", display_abi(abi))?;
299 }
300
301 write!(
302 f,
303 "fn({})",
304 display_separated_list(
305 fn_sig
306 .argument_types
307 .iter()
308 .map(|type_| { display_type(type_, style, bound_lifetime_depth) }),
309 ", "
310 )
311 )?;
312
313 if let Type::Basic(BasicType::Unit) = fn_sig.return_type.as_ref() {
314 Ok(())
315 } else {
316 write!(
317 f,
318 " -> {}",
319 display_type(&fn_sig.return_type, style, bound_lifetime_depth)
320 )
321 }
322 })
323}
324
325fn display_abi<'a>(abi: &'a Abi) -> impl Display + 'a {
326 display_fn(move |f| {
327 f.write_char('"')?;
328
329 match abi {
330 Abi::C => f.write_char('C')?,
331 Abi::Named(name) => {
332 let mut iter = name.split('_');
333
334 f.write_str(iter.next().unwrap())?;
335
336 for item in iter {
337 write!(f, "-{}", item)?;
338 }
339 }
340 }
341
342 f.write_char('"')
343 })
344}
345
346fn display_dyn_bounds<'a>(dyn_bounds: &'a DynBounds, style: Style, bound_lifetime_depth: u64) -> impl Display + 'a {
347 display_fn(move |f| {
348 f.write_str("dyn ")?;
349
350 if dyn_bounds.bound_lifetimes != 0 {
351 write!(
352 f,
353 "{} ",
354 display_binder(dyn_bounds.bound_lifetimes, bound_lifetime_depth)
355 )?;
356 }
357
358 let bound_lifetime_depth = bound_lifetime_depth + dyn_bounds.bound_lifetimes;
359
360 display_separated_list(
361 dyn_bounds
362 .dyn_traits
363 .iter()
364 .map(move |dyn_trait| display_dyn_trait(dyn_trait, style, bound_lifetime_depth)),
365 " + ",
366 )
367 .fmt(f)
368 })
369}
370
371fn display_dyn_trait<'a>(dyn_trait: &'a DynTrait, style: Style, bound_lifetime_depth: u64) -> impl Display + 'a {
372 display_fn(move |f| {
373 if dyn_trait.dyn_trait_assoc_bindings.is_empty() {
374 display_path(&dyn_trait.path, style, bound_lifetime_depth, false).fmt(f)
375 } else if let Path::Generic { path, generic_args } = dyn_trait.path.as_ref() {
376 write!(
377 f,
378 "{}<{}>",
379 display_path(path, style, bound_lifetime_depth, false),
380 display_separated_list(
381 generic_args
382 .iter()
383 .map(Ok)
384 .chain(dyn_trait.dyn_trait_assoc_bindings.iter().map(Err))
385 .map(|value| {
386 display_fn(move |f| match value {
387 Ok(generic_arg) => display_generic_arg(generic_arg, style, bound_lifetime_depth).fmt(f),
388 Err(dyn_trait_assoc_binding) => display_dyn_trait_assoc_binding(
389 dyn_trait_assoc_binding,
390 style,
391 bound_lifetime_depth,
392 )
393 .fmt(f),
394 })
395 }),
396 ", "
397 )
398 )
399 } else {
400 write!(
401 f,
402 "{}<{}>",
403 display_path(&dyn_trait.path, style, bound_lifetime_depth, false),
404 display_separated_list(
405 dyn_trait
406 .dyn_trait_assoc_bindings
407 .iter()
408 .map(|dyn_trait_assoc_binding| {
409 display_dyn_trait_assoc_binding(dyn_trait_assoc_binding, style, bound_lifetime_depth)
410 }),
411 ", "
412 )
413 )
414 }
415 })
416}
417
418fn display_dyn_trait_assoc_binding<'a>(
419 dyn_trait_assoc_binding: &'a DynTraitAssocBinding,
420 style: Style,
421 bound_lifetime_depth: u64,
422) -> impl Display + 'a {
423 display_fn(move |f| {
424 write!(
425 f,
426 "{} = {}",
427 dyn_trait_assoc_binding.name,
428 display_type(&dyn_trait_assoc_binding.type_, style, bound_lifetime_depth)
429 )
430 })
431}
432
433fn write_integer<T: Display>(f: &mut Formatter, value: T, style: Style) -> fmt::Result {
434 write!(f, "{}", value)?;
435
436 if matches!(style, Style::Long) {
437 f.write_str(any::type_name::<T>())
438 } else {
439 Ok(())
440 }
441}
442
443pub fn display_const<'a>(
444 const_: &'a Const,
445 style: Style,
446 bound_lifetime_depth: u64,
447 in_value: bool,
448) -> impl Display + 'a {
449 display_fn(move |f| match *const_ {
450 Const::I8(value) => write_integer(f, value, style),
451 Const::U8(value) => write_integer(f, value, style),
452 Const::Isize(value) => write_integer(f, value, style),
453 Const::Usize(value) => write_integer(f, value, style),
454 Const::I32(value) => write_integer(f, value, style),
455 Const::U32(value) => write_integer(f, value, style),
456 Const::I128(value) => write_integer(f, value, style),
457 Const::U128(value) => write_integer(f, value, style),
458 Const::I16(value) => write_integer(f, value, style),
459 Const::U16(value) => write_integer(f, value, style),
460 Const::I64(value) => write_integer(f, value, style),
461 Const::U64(value) => write_integer(f, value, style),
462 Const::Bool(value) => write!(f, "{}", value),
463 Const::Char(value) => write!(f, "{:?}", value),
464 Const::Str(ref value) => {
465 if in_value {
466 write!(f, "*{:?}", value)
467 } else {
468 write!(f, "{{*{:?}}}", value)
469 }
470 }
471 Const::Ref(ref value) => {
472 if let Const::Str(value) = value.as_ref() {
473 write!(f, "{:?}", value)
474 } else if in_value {
475 write!(f, "&{}", display_const(value, style, bound_lifetime_depth, true))
476 } else {
477 write!(f, "{{&{}}}", display_const(value, style, bound_lifetime_depth, true))
478 }
479 }
480 Const::RefMut(ref value) => {
481 let inner = display_const(value, style, bound_lifetime_depth, true);
482
483 if in_value {
484 write!(f, "&mut {}", inner)
485 } else {
486 write!(f, "{{&mut {}}}", inner)
487 }
488 }
489 Const::Array(ref items) => {
490 let inner = display_separated_list(
491 items
492 .iter()
493 .map(|item| display_const(item, style, bound_lifetime_depth, true)),
494 ", ",
495 );
496
497 if in_value {
498 write!(f, "[{}]", inner)
499 } else {
500 write!(f, "{{[{}]}}", inner)
501 }
502 }
503 Const::Tuple(ref items) => {
504 let inner = display_separated_list(
505 items
506 .iter()
507 .map(|item| display_const(item, style, bound_lifetime_depth, true)),
508 ", ",
509 );
510
511 if in_value {
512 write!(f, "({}", inner)?;
513
514 f.write_str(if items.len() == 1 { ",)" } else { ")" })
515 } else {
516 write!(f, "{{({}", inner)?;
517
518 f.write_str(if items.len() == 1 { ",)}" } else { ")}" })
519 }
520 }
521 Const::NamedStruct { ref path, ref fields } => {
522 let path = display_path(path, style, bound_lifetime_depth, true);
523 let fields = display_const_fields(fields, style, bound_lifetime_depth);
524
525 if in_value {
526 write!(f, "{}{}", path, fields)
527 } else {
528 write!(f, "{{{}{}}}", path, fields)
529 }
530 }
531 Const::Placeholder => write!(f, "_"),
532 })
533}
534
535fn display_const_fields<'a>(fields: &'a ConstFields, style: Style, bound_lifetime_depth: u64) -> impl Display + 'a {
536 display_fn(move |f| match fields {
537 ConstFields::Unit => Ok(()),
538 ConstFields::Tuple(fields) => write!(
539 f,
540 "({})",
541 display_separated_list(
542 fields
543 .iter()
544 .map(|field| display_const(field, style, bound_lifetime_depth, true)),
545 ", "
546 )
547 ),
548 ConstFields::Struct(fields) => {
549 if fields.is_empty() {
550 write!(f, " {{ }}")
552 } else {
553 write!(
554 f,
555 " {{ {} }}",
556 display_separated_list(
557 fields.iter().map(|(name, value)| {
558 display_fn(move |f| {
559 write!(
560 f,
561 "{}: {}",
562 name,
563 display_const(value, style, bound_lifetime_depth, true)
564 )
565 })
566 }),
567 ", "
568 )
569 )
570 }
571 }
572 })
573}
574
575#[cfg(test)]
576mod tests {
577 use super::Style;
578 use crate::rust_v0::Symbol;
579 use std::fmt::Write;
580
581 #[test]
582 fn test_display_path() {
583 let test_cases = [(
584 "_RINvCsd5QWgxammnl_7example3fooNcNtINtNtCs454gRYH7d6L_4core6result6ResultllE2Ok0EB2_",
585 (
586 "foo::<Result<i32, i32>::Ok>",
587 "example::foo::<core::result::Result<i32, i32>::Ok>",
588 "example[9884ce86676676d1]::foo::<core[2f8af133219d6c11]::result::Result<i32, i32>::Ok>",
589 ),
590 )];
591
592 let mut buffer = String::new();
593
594 for (symbol, expected) in test_cases {
595 let symbol = Symbol::parse_from_str(symbol).unwrap().0;
596
597 write!(buffer, "{}", symbol.display(Style::Short)).unwrap();
598
599 let length_1 = buffer.len();
600
601 write!(buffer, "{}", symbol.display(Style::Normal)).unwrap();
602
603 let length_2 = buffer.len();
604
605 write!(buffer, "{}", symbol.display(Style::Long)).unwrap();
606
607 assert_eq!(
608 (&buffer[..length_1], &buffer[length_1..length_2], &buffer[length_2..]),
609 expected
610 );
611
612 buffer.clear();
613 }
614 }
615
616 #[test]
617 fn test_display_lifetime() {
618 #[track_caller]
619 fn check(lifetime: u64, bound_lifetime_depth: u64, expected: &str) {
620 assert_eq!(
621 super::display_lifetime(lifetime, bound_lifetime_depth).to_string(),
622 expected
623 );
624 }
625
626 check(0, 0, "'_");
627 check(0, 1, "'_");
628 check(0, 2, "'_");
629
630 check(1, 1, "'a");
631 check(1, 2, "'b");
632 check(1, 3, "'c");
633
634 check(2, 2, "'a");
635 check(2, 3, "'b");
636 check(2, 4, "'c");
637 }
638
639 #[test]
640 fn test_display_binder() {
641 #[track_caller]
642 fn check(bound_lifetimes: u64, bound_lifetime_depth: u64, expected: &str) {
643 assert_eq!(
644 super::display_binder(bound_lifetimes, bound_lifetime_depth).to_string(),
645 expected
646 );
647 }
648
649 check(0, 0, "for<>");
650 check(0, 1, "for<>");
651 check(0, 2, "for<>");
652
653 check(1, 0, "for<'a>");
654 check(1, 1, "for<'b>");
655 check(1, 2, "for<'c>");
656
657 check(2, 0, "for<'a, 'b>");
658 check(2, 1, "for<'b, 'c>");
659 check(2, 2, "for<'c, 'd>");
660 }
661}