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