Skip to main content

luaur_analysis/functions/
to_string_human.rs

1//! Source: `Analysis/src/TypePath.cpp:742-967` (hand-ported)
2use crate::enums::pack_field::PackField;
3use crate::enums::type_field::TypeField;
4use crate::enums::variant::Variant;
5use crate::functions::to_human_readable_index::to_human_readable_index;
6use crate::records::path::Path;
7use crate::type_aliases::component::Component;
8use alloc::string::String;
9use core::fmt::Write as _;
10
11#[derive(Clone, Copy, PartialEq, Eq)]
12enum State {
13    Initial,
14    Normal,
15    Property,
16    PendingIs,
17    PendingAs,
18    PendingWhich,
19}
20
21pub fn to_string_human(path: &Path) -> String {
22    let mut result = String::new();
23    let mut state = State::Initial;
24    let mut last = false;
25
26    let mut count: usize = 0;
27
28    for component in &path.components {
29        count += 1;
30        if count == path.components.len() {
31            last = true;
32        }
33
34        match component {
35            Component::Property(c) => {
36                if state == State::PendingIs {
37                    result.push_str(", ");
38                }
39
40                match state {
41                    State::Initial | State::PendingIs => {
42                        if c.is_read {
43                            result.push_str("accessing `");
44                        } else {
45                            result.push_str("writing to `");
46                        }
47                    }
48                    State::Property => {
49                        // if the previous state was a property, then we're doing
50                        // a sequence of indexing
51                        result.push('.');
52                    }
53                    _ => {}
54                }
55
56                result.push_str(&c.name);
57
58                state = State::Property;
59            }
60            Component::Index(c) => {
61                if state == State::Initial && !last {
62                    result.push_str("in ");
63                } else if state == State::PendingIs {
64                    result.push_str(" has ");
65                } else if state == State::Property {
66                    result.push_str("` has ");
67                }
68
69                let _ = write!(result, "the {}", to_human_readable_index(c.index));
70
71                match c.variant {
72                    Variant::Pack => result.push_str(" entry in the type pack"),
73                    Variant::Union => result.push_str(" component of the union"),
74                    Variant::Intersection => result.push_str(" component of the intersection"),
75                }
76
77                if state == State::PendingWhich {
78                    result.push_str(" which");
79                }
80
81                if state == State::PendingIs || state == State::Property {
82                    state = State::PendingAs;
83                } else {
84                    state = State::PendingIs;
85                }
86            }
87            Component::TypeField(c) => {
88                if state == State::Initial && !last {
89                    result.push_str("in ");
90                } else if state == State::PendingIs {
91                    result.push_str(", ");
92                } else if state == State::Property {
93                    result.push_str("` has ");
94                }
95
96                match c {
97                    TypeField::Table => {
98                        result.push_str("the table portion");
99                        if state == State::Property {
100                            state = State::PendingAs;
101                        } else {
102                            state = State::PendingIs;
103                        }
104                    }
105                    TypeField::Metatable => {
106                        result.push_str("the metatable portion");
107                        if state == State::Property {
108                            state = State::PendingAs;
109                        } else {
110                            state = State::PendingIs;
111                        }
112                    }
113                    TypeField::LowerBound => {
114                        result.push_str("the lower bound of ");
115                        state = State::Normal;
116                    }
117                    TypeField::UpperBound => {
118                        result.push_str("the upper bound of ");
119                        state = State::Normal;
120                    }
121                    TypeField::IndexLookup => {
122                        result.push_str("the index type");
123                        if state == State::Property {
124                            state = State::PendingAs;
125                        } else {
126                            state = State::PendingIs;
127                        }
128                    }
129                    TypeField::IndexResult => {
130                        result.push_str("the result of indexing");
131                        if state == State::Property {
132                            state = State::PendingAs;
133                        } else {
134                            state = State::PendingIs;
135                        }
136                    }
137                    TypeField::Negated => {
138                        result.push_str("the negation ");
139                        state = State::Normal;
140                    }
141                    TypeField::Variadic => {
142                        result.push_str("the variadic ");
143                        state = State::Normal;
144                    }
145                }
146            }
147            Component::PackField(c) => {
148                if state == State::PendingIs {
149                    result.push_str(", ");
150                } else if state == State::Property {
151                    result.push_str("`, ");
152                }
153
154                match c {
155                    PackField::Arguments => {
156                        if state == State::Initial {
157                            result.push_str("it ");
158                        } else if state == State::PendingIs {
159                            result.push_str("the function ");
160                        }
161                        result.push_str("takes");
162                    }
163                    PackField::Returns => {
164                        if state == State::Initial {
165                            result.push_str("it ");
166                        } else if state == State::PendingIs {
167                            result.push_str("the function ");
168                        }
169                        result.push_str("returns");
170                    }
171                    PackField::Tail => {
172                        if state == State::Initial {
173                            result.push_str("it has ");
174                        }
175                        result.push_str("a tail of");
176                    }
177                }
178
179                if state == State::PendingIs {
180                    result.push(' ');
181                    state = State::PendingWhich;
182                } else {
183                    result.push(' ');
184                    state = State::Normal;
185                }
186            }
187            Component::PackSlice(c) => {
188                let _ = write!(
189                    result,
190                    "the portion of the type pack starting at index {} to the end",
191                    c.start_index
192                );
193            }
194            Component::Reduction(_c) => {
195                if state == State::Initial {
196                    result.push_str("it ");
197                }
198                result.push_str("reduces to ");
199                state = State::Normal;
200            }
201            Component::GenericPackMapping(_c) => {
202                result.push_str("is a generic pack mapped to ");
203            }
204        }
205    }
206
207    match state {
208        State::Property => {
209            result.push_str("` results in ");
210        }
211        State::PendingWhich => {
212            // pending `which` becomes `is` if it's at the end
213            result.push_str("is ");
214        }
215        State::PendingIs => {
216            result.push_str(" is ");
217        }
218        State::PendingAs => {
219            result.push_str(" as ");
220        }
221        _ => {}
222    }
223
224    result
225}