wasmtime_wit_bindgen/
rust.rs

1use crate::{types::TypeInfo, Ownership};
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        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.
383pub 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.
443pub 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}