1use id_arena::Arena;
2use indexmap::IndexMap;
3
4use crate::{Type, TypeId, substitute};
5
6pub fn stringify(arena: &mut Arena<Type>, id: TypeId) -> String {
7 let id = substitute(arena, id);
8 stringify_impl(arena, id, &mut IndexMap::new())
9}
10
11pub fn stringify_without_substitution(arena: &Arena<Type>, id: TypeId) -> String {
12 stringify_impl(arena, id, &mut IndexMap::new())
13}
14
15pub(crate) fn stringify_impl(
16 arena: &Arena<Type>,
17 id: TypeId,
18 stack: &mut IndexMap<TypeId, bool>,
19) -> String {
20 let len = stack.len();
21
22 if stack.contains_key(&id) {
23 stack.insert(id, true);
24 return format!("<{}>", stack.get_index_of(&id).unwrap());
25 }
26
27 stack.insert(id, false);
28
29 let result = match arena[id].clone() {
30 Type::Ref(id) => stringify_impl(arena, id, stack),
31 Type::Unresolved => "{unresolved}".to_string(),
32 Type::Generic(generic) => {
33 if let Some(name) = generic.name {
34 name.text().to_string()
35 } else {
36 format!("{{generic {}}}", id.index())
37 }
38 }
39 Type::Never => "Never".to_string(),
40 Type::Any => "Any".to_string(),
41 Type::Atom(atom) => atom.to_string(),
42 Type::Pair(pair) => {
43 let first = stringify_impl(arena, pair.first, stack);
44 let rest = stringify_impl(arena, pair.rest, stack);
45 format!("({first}, {rest})")
46 }
47 Type::Struct(ty) => {
48 if let Some(name) = ty.name {
49 name.text().to_string()
50 } else {
51 stringify_impl(arena, ty.inner, stack)
52 }
53 }
54 Type::Alias(alias) => {
55 if let Some(name) = alias.name {
56 name.text().to_string()
57 } else {
58 stringify_impl(arena, alias.inner, stack)
59 }
60 }
61 Type::Apply(apply) => {
62 let inner = stringify_impl(arena, apply.inner, stack);
63 let generics = apply
64 .generics
65 .iter()
66 .map(|(k, v)| {
67 format!(
68 "{}: {}",
69 stringify_impl(arena, *k, stack),
70 stringify_impl(arena, *v, stack)
71 )
72 })
73 .collect::<Vec<_>>()
74 .join(", ");
75 format!("{inner}<{generics}>")
76 }
77 Type::Function(function) => {
78 let params = function
79 .params
80 .iter()
81 .map(|id| stringify_impl(arena, *id, stack))
82 .collect::<Vec<_>>()
83 .join(", ");
84 let ret = stringify_impl(arena, function.ret, stack);
85 format!("fn({params}) -> {ret}")
86 }
87 Type::Union(union) => union
88 .types
89 .iter()
90 .map(|id| stringify_impl(arena, *id, stack))
91 .collect::<Vec<_>>()
92 .join(" | "),
93 };
94
95 let recursed = stack.pop().unwrap().1;
96
97 if recursed {
98 if result.starts_with('(') {
99 format!("{len} @ {result}")
100 } else {
101 format!("{len} @ ({result})")
102 }
103 } else {
104 result
105 }
106}