1use crate::{InferredType, TypeInternal};
16use golem_wasm_ast::analysis::AnalysedType;
17use std::fmt;
18use std::ops::Deref;
19
20pub trait GetTypeHint {
28 fn get_type_hint(&self) -> TypeHint;
29}
30
31#[derive(PartialEq, Clone, Debug)]
32pub enum TypeHint {
33 Record(Option<Vec<(String, TypeHint)>>),
34 Tuple(Option<Vec<TypeHint>>),
35 Flag(Option<Vec<String>>),
36 Str,
37 Number,
38 List(Option<Box<TypeHint>>),
39 Boolean,
40 Option(Option<Box<TypeHint>>),
41 Enum(Option<Vec<String>>),
42 Char,
43 Result {
44 ok: Option<Box<TypeHint>>,
45 err: Option<Box<TypeHint>>,
46 },
47 Resource,
48 Variant(Option<Vec<(String, Option<TypeHint>)>>),
49 Unknown,
50 Ambiguous {
51 possibilities: Vec<TypeHint>,
52 },
53 Range,
54}
55
56impl TypeHint {
57 pub fn get_type_kind(&self) -> String {
58 match self {
59 TypeHint::Record(_) => "record".to_string(),
60 TypeHint::Tuple(_) => "tuple".to_string(),
61 TypeHint::Flag(_) => "flag".to_string(),
62 TypeHint::Str => "str".to_string(),
63 TypeHint::Number => "number".to_string(),
64 TypeHint::List(_) => "list".to_string(),
65 TypeHint::Boolean => "boolean".to_string(),
66 TypeHint::Option(_) => "option".to_string(),
67 TypeHint::Enum(_) => "enum".to_string(),
68 TypeHint::Char => "char".to_string(),
69 TypeHint::Result { .. } => "result".to_string(),
70 TypeHint::Resource => "resource".to_string(),
71 TypeHint::Variant(_) => "variant".to_string(),
72 TypeHint::Unknown => "unknown".to_string(),
73 TypeHint::Ambiguous { .. } => "ambiguous".to_string(),
74 TypeHint::Range => "range".to_string(),
75 }
76 }
77}
78
79impl fmt::Display for TypeHint {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 match self {
82 TypeHint::Record(Some(fields)) => {
83 write!(f, "record{{")?;
84 for (i, (name, kind)) in fields.iter().enumerate() {
85 if i > 0 {
86 write!(f, ", ")?;
87 }
88 write!(f, "{name}: {kind}")?;
89 }
90 write!(f, "}}")
91 }
92 TypeHint::Record(None) => write!(f, "record"),
93
94 TypeHint::Tuple(Some(types)) => {
95 write!(f, "tuple<")?;
96 for (i, kind) in types.iter().enumerate() {
97 if i > 0 {
98 write!(f, ", ")?;
99 }
100 write!(f, "{kind}")?;
101 }
102 write!(f, ">")
103 }
104 TypeHint::Tuple(None) => write!(f, "tuple"),
105
106 TypeHint::Flag(Some(flags)) => {
107 write!(f, "{{")?;
108 for (i, flag) in flags.iter().enumerate() {
109 if i > 0 {
110 write!(f, ", ")?;
111 }
112 write!(f, "{flag}")?;
113 }
114 write!(f, "}}")
115 }
116 TypeHint::Flag(None) => write!(f, "flag"),
117
118 TypeHint::Str => write!(f, "string"),
119 TypeHint::Number => write!(f, "number"),
120 TypeHint::List(None) => write!(f, "list"),
121 TypeHint::List(Some(typ)) => {
122 write!(f, "list<")?;
123 write!(f, "{typ}")?;
124 write!(f, ">")
125 }
126 TypeHint::Boolean => write!(f, "boolean"),
127 TypeHint::Option(None) => write!(f, "option"),
128 TypeHint::Option(Some(inner)) => {
129 write!(f, "option<")?;
130 write!(f, "{}", inner.deref())?;
131 write!(f, ">")
132 }
133 TypeHint::Enum(None) => write!(f, "enum"),
134 TypeHint::Enum(Some(enums)) => {
135 write!(f, "enum{{")?;
136 for (i, enum_name) in enums.iter().enumerate() {
137 if i > 0 {
138 write!(f, ", ")?;
139 }
140 write!(f, "{enum_name}")?;
141 }
142 write!(f, "}}")
143 }
144 TypeHint::Char => write!(f, "char"),
145 TypeHint::Result { ok, err } => {
146 write!(f, "result<")?;
147 if let Some(ok) = ok {
148 write!(f, "{ok}")?;
149 } else {
150 write!(f, "_")?;
151 }
152 write!(f, ", ")?;
153 if let Some(err) = err {
154 write!(f, "{err}")?;
155 } else {
156 write!(f, "_")?;
157 }
158 write!(f, ">")
159 }
160 TypeHint::Resource => write!(f, "resource"),
161 TypeHint::Variant(Some(variants)) => {
162 write!(f, "variant{{")?;
163 for (i, (name, kind)) in variants.iter().enumerate() {
164 if i > 0 {
165 write!(f, ", ")?;
166 }
167 write!(
168 f,
169 "{}: {}",
170 name,
171 kind.clone().map_or("_".to_string(), |x| x.to_string())
172 )?;
173 }
174 write!(f, "}}")
175 }
176 TypeHint::Variant(None) => write!(f, "variant"),
177 TypeHint::Unknown => write!(f, "unknown"),
178 TypeHint::Range => write!(f, "range"),
179
180 TypeHint::Ambiguous { possibilities } => {
181 write!(f, "conflicting types: ")?;
182 for (i, kind) in possibilities.iter().enumerate() {
183 if i > 0 {
184 write!(f, ", ")?;
185 }
186 write!(f, "{kind}")?;
187 }
188 Ok(())
189 }
190 }
191 }
192}
193
194impl GetTypeHint for AnalysedType {
195 fn get_type_hint(&self) -> TypeHint {
196 match self {
197 AnalysedType::Record(fields) => {
198 let fields = fields
199 .fields
200 .iter()
201 .map(|name_tpe| (name_tpe.name.clone(), name_tpe.typ.get_type_hint()))
202 .collect();
203 TypeHint::Record(Some(fields))
204 }
205 AnalysedType::Tuple(elems) => {
206 let elems = elems.items.iter().map(|tpe| tpe.get_type_hint()).collect();
207 TypeHint::Tuple(Some(elems))
208 }
209 AnalysedType::Flags(flags) => {
210 let flags = flags.names.clone();
211 TypeHint::Flag(Some(flags))
212 }
213 AnalysedType::Str(_) => TypeHint::Str,
214 AnalysedType::S8(_) => TypeHint::Number,
215 AnalysedType::U8(_) => TypeHint::Number,
216 AnalysedType::S16(_) => TypeHint::Number,
217 AnalysedType::U16(_) => TypeHint::Number,
218 AnalysedType::S32(_) => TypeHint::Number,
219 AnalysedType::U32(_) => TypeHint::Number,
220 AnalysedType::S64(_) => TypeHint::Number,
221 AnalysedType::U64(_) => TypeHint::Number,
222 AnalysedType::F32(_) => TypeHint::Number,
223 AnalysedType::F64(_) => TypeHint::Number,
224 AnalysedType::Chr(_) => TypeHint::Char,
225 AnalysedType::List(tpe) => {
226 let inner = tpe.inner.get_type_hint();
227 TypeHint::List(Some(Box::new(inner)))
228 }
229 AnalysedType::Bool(_) => TypeHint::Boolean,
230 AnalysedType::Option(tpe) => {
231 let inner = tpe.inner.get_type_hint();
232 TypeHint::Option(Some(Box::new(inner)))
233 }
234 AnalysedType::Enum(tpe) => {
235 let variants = tpe.cases.clone();
236 TypeHint::Enum(Some(variants))
237 }
238 AnalysedType::Result(tpe_result) => {
239 let ok: &Option<Box<AnalysedType>> = &tpe_result.ok;
240 let err: &Option<Box<AnalysedType>> = &tpe_result.err;
241 let ok = ok.as_ref().map(|tpe| tpe.get_type_hint());
242 let err = err.as_ref().map(|tpe| tpe.get_type_hint());
243 TypeHint::Result {
244 ok: ok.map(Box::new),
245 err: err.map(Box::new),
246 }
247 }
248 AnalysedType::Handle(_) => TypeHint::Resource,
249 AnalysedType::Variant(variants) => {
250 let variants = variants
251 .cases
252 .iter()
253 .map(|name_tpe| {
254 (
255 name_tpe.name.clone(),
256 name_tpe.typ.clone().map(|tpe| tpe.get_type_hint()),
257 )
258 })
259 .collect();
260 TypeHint::Variant(Some(variants))
261 }
262 }
263 }
264}
265
266impl GetTypeHint for InferredType {
267 fn get_type_hint(&self) -> TypeHint {
268 match self.internal_type() {
269 TypeInternal::Bool => TypeHint::Boolean,
270 TypeInternal::S8
271 | TypeInternal::U8
272 | TypeInternal::S16
273 | TypeInternal::U16
274 | TypeInternal::S32
275 | TypeInternal::U32
276 | TypeInternal::S64
277 | TypeInternal::U64
278 | TypeInternal::F32
279 | TypeInternal::F64 => TypeHint::Number,
280 TypeInternal::Chr => TypeHint::Char,
281 TypeInternal::Str => TypeHint::Str,
282 TypeInternal::List(inferred_type) => {
283 TypeHint::List(Some(Box::new(inferred_type.get_type_hint())))
284 }
285 TypeInternal::Tuple(tuple) => {
286 TypeHint::Tuple(Some(tuple.iter().map(GetTypeHint::get_type_hint).collect()))
287 }
288 TypeInternal::Record(record) => TypeHint::Record(Some(
289 record
290 .iter()
291 .map(|(name, tpe)| (name.to_string(), tpe.get_type_hint()))
292 .collect(),
293 )),
294 TypeInternal::Flags(flags) => {
295 TypeHint::Flag(Some(flags.iter().map(|x| x.to_string()).collect()))
296 }
297 TypeInternal::Enum(enums) => {
298 TypeHint::Enum(Some(enums.iter().map(|s| s.to_string()).collect()))
299 }
300 TypeInternal::Option(inner) => TypeHint::Option(Some(Box::new(inner.get_type_hint()))),
301 TypeInternal::Result { ok, error } => TypeHint::Result {
302 ok: ok.as_ref().map(|tpe| Box::new(tpe.get_type_hint())),
303 err: error.as_ref().map(|tpe| Box::new(tpe.get_type_hint())),
304 },
305 TypeInternal::Variant(variants) => TypeHint::Variant(Some(
306 variants
307 .iter()
308 .map(|(name, tpe)| {
309 (
310 name.to_string(),
311 tpe.as_ref().map(GetTypeHint::get_type_hint),
312 )
313 })
314 .collect(),
315 )),
316 TypeInternal::Resource { .. } => TypeHint::Resource,
317 TypeInternal::AllOf(possibilities) => get_type_kind(possibilities),
318 TypeInternal::Unknown | TypeInternal::Sequence(_) | TypeInternal::Instance { .. } => {
319 TypeHint::Unknown
320 }
321 TypeInternal::Range { .. } => TypeHint::Range,
322 }
323 }
324}
325
326fn get_type_kind(possibilities: &[InferredType]) -> TypeHint {
327 if let Some(first) = possibilities.first() {
328 let first = first.get_type_hint();
329 if possibilities.iter().all(|p| p.get_type_hint() == first) {
330 first
331 } else {
332 TypeHint::Ambiguous {
333 possibilities: possibilities.iter().map(|p| p.get_type_hint()).collect(),
334 }
335 }
336 } else {
337 TypeHint::Unknown
338 }
339}