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