rue_types/
stringify.rs

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}