Skip to main content

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                .values()
66                .map(|v| stringify_impl(arena, *v, stack))
67                .collect::<Vec<_>>()
68                .join(", ");
69            format!("{inner}<{generics}>")
70        }
71        Type::Function(function) => {
72            let params = function
73                .params
74                .iter()
75                .map(|(name, id)| format!("{}: {}", name, stringify_impl(arena, *id, stack)))
76                .collect::<Vec<_>>()
77                .join(", ");
78            let ret = stringify_impl(arena, function.ret, stack);
79            format!("fn({params}) -> {ret}")
80        }
81        Type::Union(union) => union
82            .types
83            .iter()
84            .map(|id| stringify_impl(arena, *id, stack))
85            .collect::<Vec<_>>()
86            .join(" | "),
87    };
88
89    let recursed = stack.pop().unwrap().1;
90
91    if recursed {
92        if result.starts_with('(') {
93            format!("{len} @ {result}")
94        } else {
95            format!("{len} @ ({result})")
96        }
97    } else {
98        result
99    }
100}