1 use crate::{types::TypeInfo, Ownership};
2 use heck::*;
3 use wit_parser::*;
4 
5 #[derive(Debug, Copy, Clone, PartialEq)]
6 pub enum TypeMode {
7     Owned,
8     AllBorrowed(&'static str),
9 }
10 
11 pub trait RustGenerator<'a> {
12     fn resolve(&self) -> &'a Resolve;
13 
14     fn push_str(&mut self, s: &str);
15     fn info(&self, ty: TypeId) -> TypeInfo;
16     fn path_to_interface(&self, interface: InterfaceId) -> Option<String>;
17     fn is_imported_interface(&self, interface: InterfaceId) -> bool;
18 
19     /// This determines whether we generate owning types or (where appropriate)
20     /// borrowing types.
21     ///
22     /// For example, when generating a type which is only used as a parameter to
23     /// a guest-exported function, there is no need for it to own its fields.
24     /// However, constructing deeply-nested borrows (e.g. `&[&[&[&str]]]]` for
25     /// `list<list<list<string>>>`) can be very awkward, so by default we
26     /// generate owning types and use only shallow borrowing at the top level
27     /// inside function signatures.
28     fn ownership(&self) -> Ownership;
29 
30     fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
31         match ty {
32             Type::Id(t) => self.print_tyid(*t, mode),
33             Type::Bool => self.push_str("bool"),
34             Type::U8 => self.push_str("u8"),
35             Type::U16 => self.push_str("u16"),
36             Type::U32 => self.push_str("u32"),
37             Type::U64 => self.push_str("u64"),
38             Type::S8 => self.push_str("i8"),
39             Type::S16 => self.push_str("i16"),
40             Type::S32 => self.push_str("i32"),
41             Type::S64 => self.push_str("i64"),
42             Type::F32 => self.push_str("f32"),
43             Type::F64 => self.push_str("f64"),
44             Type::Char => self.push_str("char"),
45             Type::String => match mode {
46                 TypeMode::AllBorrowed(lt) => {
47                     self.push_str("&");
48                     if lt != "'_" {
49                         self.push_str(lt);
50                         self.push_str(" ");
51                     }
52                     self.push_str("str");
53                 }
54                 TypeMode::Owned => self.push_str("wasmtime::component::__internal::String"),
55             },
56         }
57     }
58 
59     fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
60         match ty {
61             Some(ty) => self.print_ty(ty, mode),
62             None => self.push_str("()"),
63         }
64     }
65 
66     fn print_tyid(&mut self, id: TypeId, mode: TypeMode) {
67         let info = self.info(id);
68         let lt = self.lifetime_for(&info, mode);
69         let ty = &self.resolve().types[id];
70         if ty.name.is_some() {
71             // If this type has a list internally, no lifetime is being printed,
72             // but we're in a borrowed mode, then that means we're in a borrowed
73             // context and don't want ownership of the type but we're using an
74             // owned type definition. Inject a `&` in front to indicate that, at
75             // the API level, ownership isn't required.
76             if info.has_list && lt.is_none() {
77                 if let TypeMode::AllBorrowed(lt) = mode {
78                     self.push_str("&");
79                     if lt != "'_" {
80                         self.push_str(lt);
81                         self.push_str(" ");
82                     }
83                 }
84             }
85             let name = if lt.is_some() {
86                 self.param_name(id)
87             } else {
88                 self.result_name(id)
89             };
90             self.print_type_name_in_interface(ty.owner, &name);
91 
92             // If the type recursively owns data and it's a
93             // variant/record/list, then we need to place the
94             // lifetime parameter on the type as well.
95             if info.has_list && needs_generics(self.resolve(), &ty.kind) {
96                 self.print_generics(lt);
97             }
98 
99             return;
100 
101             fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool {
102                 match ty {
103                     TypeDefKind::Variant(_)
104                     | TypeDefKind::Record(_)
105                     | TypeDefKind::Option(_)
106                     | TypeDefKind::Result(_)
107                     | TypeDefKind::Future(_)
108                     | TypeDefKind::Stream(_)
109                     | TypeDefKind::List(_)
110                     | TypeDefKind::Flags(_)
111                     | TypeDefKind::Enum(_)
112                     | TypeDefKind::Tuple(_)
113                     | TypeDefKind::Handle(_)
114                     | TypeDefKind::Resource => true,
115                     TypeDefKind::Type(Type::Id(t)) => {
116                         needs_generics(resolve, &resolve.types[*t].kind)
117                     }
118                     TypeDefKind::Type(Type::String) => true,
119                     TypeDefKind::Type(_) => false,
120                     TypeDefKind::Unknown => unreachable!(),
121                 }
122             }
123         }
124 
125         match &ty.kind {
126             TypeDefKind::List(t) => self.print_list(t, mode),
127 
128             TypeDefKind::Option(t) => {
129                 self.push_str("Option<");
130                 self.print_ty(t, mode);
131                 self.push_str(">");
132             }
133 
134             TypeDefKind::Result(r) => {
135                 self.push_str("Result<");
136                 self.print_optional_ty(r.ok.as_ref(), mode);
137                 self.push_str(",");
138                 self.print_optional_ty(r.err.as_ref(), mode);
139                 self.push_str(">");
140             }
141 
142             TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
143 
144             // Tuple-like records are mapped directly to Rust tuples of
145             // types. Note the trailing comma after each member to
146             // appropriately handle 1-tuples.
147             TypeDefKind::Tuple(t) => {
148                 self.push_str("(");
149                 for ty in t.types.iter() {
150                     self.print_ty(ty, mode);
151                     self.push_str(",");
152                 }
153                 self.push_str(")");
154             }
155             TypeDefKind::Record(_) => {
156                 panic!("unsupported anonymous type reference: record")
157             }
158             TypeDefKind::Flags(_) => {
159                 panic!("unsupported anonymous type reference: flags")
160             }
161             TypeDefKind::Enum(_) => {
162                 panic!("unsupported anonymous type reference: enum")
163             }
164             TypeDefKind::Future(ty) => {
165                 self.push_str("Future<");
166                 self.print_optional_ty(ty.as_ref(), mode);
167                 self.push_str(">");
168             }
169             TypeDefKind::Stream(stream) => {
170                 self.push_str("Stream<");
171                 self.print_optional_ty(stream.element.as_ref(), mode);
172                 self.push_str(",");
173                 self.print_optional_ty(stream.end.as_ref(), mode);
174                 self.push_str(">");
175             }
176 
177             TypeDefKind::Handle(handle) => {
178                 self.print_handle(handle);
179             }
180             TypeDefKind::Resource => unreachable!(),
181 
182             TypeDefKind::Type(t) => self.print_ty(t, mode),
183             TypeDefKind::Unknown => unreachable!(),
184         }
185     }
186 
187     fn print_type_name_in_interface(&mut self, owner: TypeOwner, name: &str) {
188         if let TypeOwner::Interface(id) = owner {
189             if let Some(path) = self.path_to_interface(id) {
190                 self.push_str(&path);
191                 self.push_str("::");
192             }
193         }
194         self.push_str(name);
195     }
196 
197     fn print_list(&mut self, ty: &Type, mode: TypeMode) {
198         let next_mode = if matches!(self.ownership(), Ownership::Owning) {
199             TypeMode::Owned
200         } else {
201             mode
202         };
203         match mode {
204             TypeMode::AllBorrowed(lt) => {
205                 self.push_str("&");
206                 if lt != "'_" {
207                     self.push_str(lt);
208                     self.push_str(" ");
209                 }
210                 self.push_str("[");
211                 self.print_ty(ty, next_mode);
212                 self.push_str("]");
213             }
214             TypeMode::Owned => {
215                 self.push_str("wasmtime::component::__internal::Vec<");
216                 self.print_ty(ty, next_mode);
217                 self.push_str(">");
218             }
219         }
220     }
221 
222     fn print_handle(&mut self, handle: &Handle) {
223         // Handles are either printed as `ResourceAny` for any guest-defined
224         // resource or `Resource<T>` for all host-defined resources. This means
225         // that this function needs to determine if `handle` points to a host
226         // or a guest resource which is determined by:
227         //
228         // * For world-owned resources, they're always imported.
229         // * For interface-owned resources, it depends on the how bindings were
230         //   last generated for this interface.
231         //
232         // Additionally type aliases via `use` are "peeled" here to find the
233         // original definition of the resource since that's the one that we
234         // care about for determining whether it's imported or not.
235         let resource = match handle {
236             Handle::Own(t) | Handle::Borrow(t) => *t,
237         };
238         let ty = &self.resolve().types[resource];
239         let def_id = super::resolve_type_definition_id(self.resolve(), resource);
240         let ty_def = &self.resolve().types[def_id];
241         let is_host_defined = match ty_def.owner {
242             TypeOwner::Interface(i) => self.is_imported_interface(i),
243             _ => true,
244         };
245         if is_host_defined {
246             self.push_str("wasmtime::component::Resource<");
247             self.print_type_name_in_interface(
248                 ty.owner,
249                 &ty.name.as_ref().unwrap().to_upper_camel_case(),
250             );
251             self.push_str(">");
252         } else {
253             self.push_str("wasmtime::component::ResourceAny");
254         }
255     }
256 
257     fn print_generics(&mut self, lifetime: Option<&str>) {
258         if lifetime.is_none() {
259             return;
260         }
261         self.push_str("<");
262         if let Some(lt) = lifetime {
263             self.push_str(lt);
264             self.push_str(",");
265         }
266         self.push_str(">");
267     }
268 
269     fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
270         let info = self.info(ty);
271         if !info.owned && !info.borrowed {
272             return Vec::new();
273         }
274         let mut result = Vec::new();
275         let first_mode =
276             if info.owned || !info.borrowed || matches!(self.ownership(), Ownership::Owning) {
277                 TypeMode::Owned
278             } else {
279                 assert!(!self.uses_two_names(&info));
280                 TypeMode::AllBorrowed("'a")
281             };
282         result.push((self.result_name(ty), first_mode));
283         if self.uses_two_names(&info) {
284             result.push((self.param_name(ty), TypeMode::AllBorrowed("'a")));
285         }
286         result
287     }
288 
289     fn param_name(&self, ty: TypeId) -> String {
290         let info = self.info(ty);
291         let name = self.resolve().types[ty]
292             .name
293             .as_ref()
294             .unwrap()
295             .to_upper_camel_case();
296         if self.uses_two_names(&info) {
297             format!("{}Param", name)
298         } else {
299             name
300         }
301     }
302 
303     fn result_name(&self, ty: TypeId) -> String {
304         let info = self.info(ty);
305         let name = self.resolve().types[ty]
306             .name
307             .as_ref()
308             .unwrap()
309             .to_upper_camel_case();
310         if self.uses_two_names(&info) {
311             format!("{}Result", name)
312         } else {
313             name
314         }
315     }
316 
317     fn uses_two_names(&self, info: &TypeInfo) -> bool {
318         info.has_list
319             && info.borrowed
320             && info.owned
321             && matches!(
322                 self.ownership(),
323                 Ownership::Borrowing {
324                     duplicate_if_necessary: true
325                 }
326             )
327     }
328 
329     fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {
330         if matches!(self.ownership(), Ownership::Owning) {
331             return None;
332         }
333         let lt = match mode {
334             TypeMode::AllBorrowed(s) => s,
335             _ => return None,
336         };
337         // No lifetimes needed unless this has a list.
338         if !info.has_list {
339             return None;
340         }
341         // If two names are used then this type will have an owned and a
342         // borrowed copy and the borrowed copy is being used, so it needs a
343         // lifetime. Otherwise if it's only borrowed and not owned then this can
344         // also use a lifetime since it's not needed in two contexts and only
345         // the borrowed version of the structure was generated.
346         if self.uses_two_names(info) || (info.borrowed && !info.owned) {
347             Some(lt)
348         } else {
349             None
350         }
351     }
352 }
353 
354 /// Translate `name` to a Rust `snake_case` identifier.
355 pub fn to_rust_ident(name: &str) -> String {
356     match name {
357         // Escape Rust keywords.
358         // Source: https://doc.rust-lang.org/reference/keywords.html
359         "as" => "as_".into(),
360         "break" => "break_".into(),
361         "const" => "const_".into(),
362         "continue" => "continue_".into(),
363         "crate" => "crate_".into(),
364         "else" => "else_".into(),
365         "enum" => "enum_".into(),
366         "extern" => "extern_".into(),
367         "false" => "false_".into(),
368         "fn" => "fn_".into(),
369         "for" => "for_".into(),
370         "if" => "if_".into(),
371         "impl" => "impl_".into(),
372         "in" => "in_".into(),
373         "let" => "let_".into(),
374         "loop" => "loop_".into(),
375         "match" => "match_".into(),
376         "mod" => "mod_".into(),
377         "move" => "move_".into(),
378         "mut" => "mut_".into(),
379         "pub" => "pub_".into(),
380         "ref" => "ref_".into(),
381         "return" => "return_".into(),
382         "self" => "self_".into(),
383         "static" => "static_".into(),
384         "struct" => "struct_".into(),
385         "super" => "super_".into(),
386         "trait" => "trait_".into(),
387         "true" => "true_".into(),
388         "type" => "type_".into(),
389         "unsafe" => "unsafe_".into(),
390         "use" => "use_".into(),
391         "where" => "where_".into(),
392         "while" => "while_".into(),
393         "async" => "async_".into(),
394         "await" => "await_".into(),
395         "dyn" => "dyn_".into(),
396         "abstract" => "abstract_".into(),
397         "become" => "become_".into(),
398         "box" => "box_".into(),
399         "do" => "do_".into(),
400         "final" => "final_".into(),
401         "macro" => "macro_".into(),
402         "override" => "override_".into(),
403         "priv" => "priv_".into(),
404         "typeof" => "typeof_".into(),
405         "unsized" => "unsized_".into(),
406         "virtual" => "virtual_".into(),
407         "yield" => "yield_".into(),
408         "try" => "try_".into(),
409         s => s.to_snake_case(),
410     }
411 }
412 
413 /// Translate `name` to a Rust `UpperCamelCase` identifier.
414 pub fn to_rust_upper_camel_case(name: &str) -> String {
415     match name {
416         // We use `Host` as the name of the trait for host implementations
417         // to fill in, so rename it if "Host" is used as a regular identifier.
418         "host" => "Host_".into(),
419         s => s.to_upper_camel_case(),
420     }
421 }
422