wasmtime_internal_wit_bindgen/
rust.rs

1use crate::{Ownership, types::TypeInfo};
2use heck::*;
3use wit_parser::*;
4
5#[derive(Debug, Copy, Clone, PartialEq)]
6pub enum TypeMode {
7    Owned,
8    AllBorrowed(&'static str),
9}
10
11pub 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        self.push_str(&self.ty(ty, mode))
33    }
34    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
69    fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
70        self.push_str(&self.optional_ty(ty, mode))
71    }
72    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
79    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::Flags(_)
125                    | TypeDefKind::Enum(_)
126                    | TypeDefKind::Tuple(_)
127                    | TypeDefKind::Handle(_)
128                    | TypeDefKind::Resource => true,
129                    TypeDefKind::Type(Type::Id(t)) => {
130                        needs_generics(resolve, &resolve.types[*t].kind)
131                    }
132                    TypeDefKind::Type(Type::String) => true,
133                    TypeDefKind::Type(_) => false,
134                    TypeDefKind::Unknown => unreachable!(),
135                    TypeDefKind::FixedSizeList(..) => todo!(),
136                }
137            }
138        }
139
140        match &ty.kind {
141            TypeDefKind::List(t) => self.list(t, mode),
142
143            TypeDefKind::Option(t) => {
144                format!("Option<{}>", self.ty(t, mode))
145            }
146
147            TypeDefKind::Result(r) => {
148                let ok = self.optional_ty(r.ok.as_ref(), mode);
149                let err = self.optional_ty(r.err.as_ref(), mode);
150                format!("Result<{ok},{err}>")
151            }
152
153            TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
154
155            // Tuple-like records are mapped directly to Rust tuples of
156            // types. Note the trailing comma after each member to
157            // appropriately handle 1-tuples.
158            TypeDefKind::Tuple(t) => {
159                let mut out = "(".to_string();
160                for ty in t.types.iter() {
161                    out.push_str(&self.ty(ty, mode));
162                    out.push_str(",");
163                }
164                out.push_str(")");
165                out
166            }
167            TypeDefKind::Record(_) => {
168                panic!("unsupported anonymous type reference: record")
169            }
170            TypeDefKind::Flags(_) => {
171                panic!("unsupported anonymous type reference: flags")
172            }
173            TypeDefKind::Enum(_) => {
174                panic!("unsupported anonymous type reference: enum")
175            }
176            TypeDefKind::Future(ty) => {
177                let wt = self.wasmtime_path();
178                let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
179                format!("{wt}::component::HostFuture<{t}>")
180            }
181            TypeDefKind::Stream(ty) => {
182                let wt = self.wasmtime_path();
183                let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
184                format!("{wt}::component::HostStream<{t}>")
185            }
186            TypeDefKind::Handle(handle) => self.handle(handle),
187            TypeDefKind::Resource => unreachable!(),
188
189            TypeDefKind::Type(t) => self.ty(t, mode),
190            TypeDefKind::Unknown => unreachable!(),
191            TypeDefKind::FixedSizeList(..) => todo!(),
192        }
193    }
194
195    fn type_name_in_interface(&self, owner: TypeOwner, name: &str) -> String {
196        let mut out = String::new();
197        if let TypeOwner::Interface(id) = owner {
198            if let Some(path) = self.path_to_interface(id) {
199                out.push_str(&path);
200                out.push_str("::");
201            }
202        }
203        out.push_str(name);
204        out
205    }
206
207    fn print_list(&mut self, ty: &Type, mode: TypeMode) {
208        self.push_str(&self.list(ty, mode))
209    }
210    fn list(&self, ty: &Type, mode: TypeMode) -> String {
211        let next_mode = if matches!(self.ownership(), Ownership::Owning) {
212            TypeMode::Owned
213        } else {
214            mode
215        };
216        let ty = self.ty(ty, next_mode);
217        match mode {
218            TypeMode::AllBorrowed(lt) => {
219                if lt != "'_" {
220                    format!("&{lt} [{ty}]")
221                } else {
222                    format!("&[{ty}]")
223                }
224            }
225            TypeMode::Owned => {
226                let wt = self.wasmtime_path();
227                format!("{wt}::component::__internal::Vec<{ty}>")
228            }
229        }
230    }
231
232    fn print_stream(&mut self, ty: Option<&Type>) {
233        self.push_str(&self.stream(ty))
234    }
235    fn stream(&self, ty: Option<&Type>) -> String {
236        let wt = self.wasmtime_path();
237        let mut out = format!("{wt}::component::HostStream<");
238        out.push_str(&self.optional_ty(ty, TypeMode::Owned));
239        out.push_str(">");
240        out
241    }
242
243    fn print_future(&mut self, ty: Option<&Type>) {
244        self.push_str(&self.future(ty))
245    }
246    fn future(&self, ty: Option<&Type>) -> String {
247        let wt = self.wasmtime_path();
248        let mut out = format!("{wt}::component::HostFuture<");
249        out.push_str(&self.optional_ty(ty, TypeMode::Owned));
250        out.push_str(">");
251        out
252    }
253
254    fn print_handle(&mut self, handle: &Handle) {
255        self.push_str(&self.handle(handle))
256    }
257    fn handle(&self, handle: &Handle) -> String {
258        // Handles are either printed as `ResourceAny` for any guest-defined
259        // resource or `Resource<T>` for all host-defined resources. This means
260        // that this function needs to determine if `handle` points to a host
261        // or a guest resource which is determined by:
262        //
263        // * For world-owned resources, they're always imported.
264        // * For interface-owned resources, it depends on the how bindings were
265        //   last generated for this interface.
266        //
267        // Additionally type aliases via `use` are "peeled" here to find the
268        // original definition of the resource since that's the one that we
269        // care about for determining whether it's imported or not.
270        let resource = match handle {
271            Handle::Own(t) | Handle::Borrow(t) => *t,
272        };
273        let ty = &self.resolve().types[resource];
274        let def_id = super::resolve_type_definition_id(self.resolve(), resource);
275        let ty_def = &self.resolve().types[def_id];
276        let is_host_defined = match ty_def.owner {
277            TypeOwner::Interface(i) => self.is_imported_interface(i),
278            _ => true,
279        };
280        let wt = self.wasmtime_path();
281        if is_host_defined {
282            let mut out = format!("{wt}::component::Resource<");
283            out.push_str(&self.type_name_in_interface(
284                ty.owner,
285                &ty.name.as_ref().unwrap().to_upper_camel_case(),
286            ));
287            out.push_str(">");
288            out
289        } else {
290            format!("{wt}::component::ResourceAny")
291        }
292    }
293
294    fn print_generics(&mut self, lifetime: Option<&str>) {
295        self.push_str(&self.generics(lifetime))
296    }
297    fn generics(&self, lifetime: Option<&str>) -> String {
298        if let Some(lt) = lifetime {
299            format!("<{lt},>")
300        } else {
301            String::new()
302        }
303    }
304
305    fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
306        let info = self.info(ty);
307        // Info only populated for types that are passed to and from functions. For
308        // types which are not, default to the ownership setting.
309        if !info.owned && !info.borrowed {
310            return vec![(
311                self.param_name(ty),
312                match self.ownership() {
313                    Ownership::Owning => TypeMode::Owned,
314                    Ownership::Borrowing { .. } => TypeMode::AllBorrowed("'a"),
315                },
316            )];
317        }
318        let mut result = Vec::new();
319        let first_mode =
320            if info.owned || !info.borrowed || matches!(self.ownership(), Ownership::Owning) {
321                TypeMode::Owned
322            } else {
323                assert!(!self.uses_two_names(&info));
324                TypeMode::AllBorrowed("'a")
325            };
326        result.push((self.result_name(ty), first_mode));
327        if self.uses_two_names(&info) {
328            result.push((self.param_name(ty), TypeMode::AllBorrowed("'a")));
329        }
330        result
331    }
332
333    fn param_name(&self, ty: TypeId) -> String {
334        let info = self.info(ty);
335        let name = self.resolve().types[ty]
336            .name
337            .as_ref()
338            .unwrap()
339            .to_upper_camel_case();
340        if self.uses_two_names(&info) {
341            format!("{name}Param")
342        } else {
343            name
344        }
345    }
346
347    fn result_name(&self, ty: TypeId) -> String {
348        let info = self.info(ty);
349        let name = self.resolve().types[ty]
350            .name
351            .as_ref()
352            .unwrap()
353            .to_upper_camel_case();
354        if self.uses_two_names(&info) {
355            format!("{name}Result")
356        } else {
357            name
358        }
359    }
360
361    fn uses_two_names(&self, info: &TypeInfo) -> bool {
362        info.has_list
363            && info.borrowed
364            && info.owned
365            && matches!(
366                self.ownership(),
367                Ownership::Borrowing {
368                    duplicate_if_necessary: true
369                }
370            )
371    }
372
373    fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {
374        if matches!(self.ownership(), Ownership::Owning) {
375            return None;
376        }
377        let lt = match mode {
378            TypeMode::AllBorrowed(s) => s,
379            _ => return None,
380        };
381        // No lifetimes needed unless this has a list.
382        if !info.has_list {
383            return None;
384        }
385        // If two names are used then this type will have an owned and a
386        // borrowed copy and the borrowed copy is being used, so it needs a
387        // lifetime. Otherwise if it's only borrowed and not owned then this can
388        // also use a lifetime since it's not needed in two contexts and only
389        // the borrowed version of the structure was generated.
390        if self.uses_two_names(info) || (info.borrowed && !info.owned) {
391            Some(lt)
392        } else {
393            None
394        }
395    }
396
397    fn typedfunc_sig(&self, func: &Function, param_mode: TypeMode) -> String {
398        let mut out = "(".to_string();
399        for (_, ty) in func.params.iter() {
400            out.push_str(&self.ty(ty, param_mode));
401            out.push_str(", ");
402        }
403        out.push_str("), (");
404        if let Some(ty) = func.result {
405            out.push_str(&self.ty(&ty, TypeMode::Owned));
406            out.push_str(", ");
407        }
408        out.push_str(")");
409        out
410    }
411}
412
413/// Translate `name` to a Rust `snake_case` identifier.
414pub fn to_rust_ident(name: &str) -> String {
415    match name {
416        // Escape Rust keywords.
417        // Source: https://doc.rust-lang.org/reference/keywords.html
418        "as" => "as_".into(),
419        "break" => "break_".into(),
420        "const" => "const_".into(),
421        "continue" => "continue_".into(),
422        "crate" => "crate_".into(),
423        "else" => "else_".into(),
424        "enum" => "enum_".into(),
425        "extern" => "extern_".into(),
426        "false" => "false_".into(),
427        "fn" => "fn_".into(),
428        "for" => "for_".into(),
429        "if" => "if_".into(),
430        "impl" => "impl_".into(),
431        "in" => "in_".into(),
432        "let" => "let_".into(),
433        "loop" => "loop_".into(),
434        "match" => "match_".into(),
435        "mod" => "mod_".into(),
436        "move" => "move_".into(),
437        "mut" => "mut_".into(),
438        "pub" => "pub_".into(),
439        "ref" => "ref_".into(),
440        "return" => "return_".into(),
441        "self" => "self_".into(),
442        "static" => "static_".into(),
443        "struct" => "struct_".into(),
444        "super" => "super_".into(),
445        "trait" => "trait_".into(),
446        "true" => "true_".into(),
447        "type" => "type_".into(),
448        "unsafe" => "unsafe_".into(),
449        "use" => "use_".into(),
450        "where" => "where_".into(),
451        "while" => "while_".into(),
452        "async" => "async_".into(),
453        "await" => "await_".into(),
454        "dyn" => "dyn_".into(),
455        "abstract" => "abstract_".into(),
456        "become" => "become_".into(),
457        "box" => "box_".into(),
458        "do" => "do_".into(),
459        "final" => "final_".into(),
460        "macro" => "macro_".into(),
461        "override" => "override_".into(),
462        "priv" => "priv_".into(),
463        "typeof" => "typeof_".into(),
464        "unsized" => "unsized_".into(),
465        "virtual" => "virtual_".into(),
466        "yield" => "yield_".into(),
467        "try" => "try_".into(),
468        "gen" => "gen_".into(),
469        s => s.to_snake_case(),
470    }
471}
472
473/// Translate `name` to a Rust `UpperCamelCase` identifier.
474pub fn to_rust_upper_camel_case(name: &str) -> String {
475    match name {
476        // We use `Host` as the name of the trait for host implementations
477        // to fill in, so rename it if "Host" is used as a regular identifier.
478        "host" => "Host_".into(),
479        s => s.to_upper_camel_case(),
480    }
481}