wit_bindgen_mbt/
lib.rs

1// This file was derived from:
2// https://github.com/bytecodealliance/wit-bindgen/blob/442f0054fef4a7ff3c7fa59cbce7f9024569c5bc/crates/rust/src/lib.rs
3// Copyright: Alex Crichton <alex@alexcrichton.com> and original contributors.
4// Modification to work with MoonBit is done by Fantix King <fantix.king@gmail.com>.
5
6use std::cmp::Ordering;
7use std::collections::{BTreeMap, HashMap, HashSet};
8use std::fmt::{self, Write as _};
9use std::mem;
10use std::str::FromStr;
11
12use anyhow::{bail, Result};
13use heck::*;
14use wit_bindgen_core::abi::{Bitcast, WasmType};
15use wit_bindgen_core::{
16    uwriteln, wit_parser::*, Direction, Files, InterfaceGenerator as _, Source, Types,
17    WorldGenerator,
18};
19
20mod bindgen;
21mod interface;
22
23#[derive(Default)]
24struct ResourceInfo {
25    // Note that a resource can be both imported and exported (e.g. when
26    // importing and exporting the same interface which contains one or more
27    // resources).  In that case, this field will be `Import` while we're
28    // importing the interface and later change to `Export` while we're
29    // exporting the interface.
30    direction: Direction,
31    owned: bool,
32}
33
34struct InterfaceName {
35    /// True when this interface name has been remapped through the use of `with` in the `bindgen!`
36    /// macro invocation.
37    remapped: bool,
38
39    /// The string name for this interface.
40    path: String,
41}
42
43#[derive(Default)]
44pub struct MoonBit {
45    types: Types,
46    src: Source,
47    opts: Opts,
48    import_modules: Vec<(String, Vec<ModuleName>)>,
49    export_modules: Vec<(String, Vec<ModuleName>)>,
50    skip: HashSet<String>,
51    interface_names: HashMap<InterfaceId, InterfaceName>,
52    resources: HashMap<TypeId, ResourceInfo>,
53    import_funcs_called: bool,
54    with_name_counter: usize,
55    export_traits: HashMap<String, String>,
56    imported_builtins: HashSet<&'static str>,
57    pub exported_symbols: HashMap<String, (String, bool)>,
58}
59
60#[derive(Clone, Eq)]
61struct ModuleName {
62    snake: String,
63    qual: String,
64}
65
66impl ModuleName {
67    fn root(name: impl AsRef<str>) -> Self {
68        let name = to_mbt_ident(name.as_ref());
69        Self {
70            snake: name.to_snake_case(),
71            qual: name.to_upper_camel_case(),
72        }
73    }
74
75    fn child(&self, name: impl AsRef<str>) -> Self {
76        let name = to_mbt_ident(name.as_ref());
77        Self {
78            snake: name.to_snake_case(),
79            qual: format!("{}{}", self.qual, name.to_upper_camel_case()),
80        }
81    }
82}
83
84impl PartialEq<Self> for ModuleName {
85    fn eq(&self, other: &Self) -> bool {
86        self.qual == other.qual
87    }
88}
89
90impl PartialOrd<Self> for ModuleName {
91    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92        self.qual.partial_cmp(&other.qual)
93    }
94}
95
96impl Ord for ModuleName {
97    fn cmp(&self, other: &Self) -> Ordering {
98        self.qual.cmp(&other.qual)
99    }
100}
101
102#[cfg(feature = "clap")]
103fn iterate_hashmap_string(s: &str) -> impl Iterator<Item = Result<(&str, &str), String>> {
104    s.split(',').map(move |entry| {
105        entry.split_once('=').ok_or_else(|| {
106            format!("expected string of form `<key>=<value>[,<key>=<value>...]`; got `{s}`")
107        })
108    })
109}
110
111#[cfg(feature = "clap")]
112fn parse_exports(s: &str) -> Result<HashMap<ExportKey, String>, String> {
113    if s.is_empty() {
114        Ok(HashMap::default())
115    } else {
116        iterate_hashmap_string(s)
117            .map(|entry| {
118                let (key, value) = entry?;
119                Ok((
120                    match key {
121                        "world" => ExportKey::World,
122                        _ => ExportKey::Name(key.to_owned()),
123                    },
124                    value.to_owned(),
125                ))
126            })
127            .collect()
128    }
129}
130
131#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
132pub enum ExportKey {
133    World,
134    Name(String),
135}
136
137#[cfg(feature = "clap")]
138fn parse_with(s: &str) -> Result<HashMap<String, String>, String> {
139    if s.is_empty() {
140        Ok(HashMap::default())
141    } else {
142        iterate_hashmap_string(s)
143            .map(|entry| {
144                let (key, value) = entry?;
145                Ok((key.to_owned(), value.to_owned()))
146            })
147            .collect()
148    }
149}
150
151#[derive(Default, Debug, Clone)]
152#[cfg_attr(feature = "clap", derive(clap::Args))]
153pub struct Opts {
154    /// If true, code generation should qualify any features that depend on
155    /// `std` with `cfg(feature = "std")`.
156    #[cfg_attr(feature = "clap", arg(long))]
157    pub std_feature: bool,
158
159    /// If true, code generation should pass borrowed string arguments as
160    /// `&[u8]` instead of `&str`. Strings are still required to be valid
161    /// UTF-8, but this avoids the need for Rust code to do its own UTF-8
162    /// validation if it doesn't already have a `&str`.
163    #[cfg_attr(feature = "clap", arg(long))]
164    pub raw_strings: bool,
165
166    /// Names of functions to skip generating bindings for.
167    #[cfg_attr(feature = "clap", arg(long))]
168    pub skip: Vec<String>,
169
170    /// Names of the concrete types which implement the traits representing any
171    /// functions, interfaces, and/or resources exported by the world.
172    ///
173    /// Example: `--exports world=MyWorld,ns:pkg/iface1=MyIface1,ns:pkg/iface1/resource1=MyResource1`,
174    #[cfg_attr(feature = "clap", arg(long, value_parser = parse_exports, default_value = ""))]
175    pub exports: HashMap<ExportKey, String>,
176
177    /// If true, generate stub implementations for any exported functions,
178    /// interfaces, and/or resources.
179    #[cfg_attr(feature = "clap", arg(long))]
180    pub stubs: bool,
181
182    /// Optionally prefix any export names with the specified value.
183    ///
184    /// This is useful to avoid name conflicts when testing.
185    #[cfg_attr(feature = "clap", arg(long))]
186    pub export_prefix: Option<String>,
187
188    /// Whether to generate owning or borrowing type definitions.
189    ///
190    /// Valid values include:
191    ///
192    /// - `owning`: Generated types will be composed entirely of owning fields,
193    /// regardless of whether they are used as parameters to imports or not.
194    ///
195    /// - `borrowing`: Generated types used as parameters to imports will be
196    /// "deeply borrowing", i.e. contain references rather than owned values
197    /// when applicable.
198    ///
199    /// - `borrowing-duplicate-if-necessary`: As above, but generating distinct
200    /// types for borrowing and owning, if necessary.
201    #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))]
202    pub ownership: Ownership,
203
204    /// The optional path to the wit-bindgen runtime module to use.
205    ///
206    /// This defaults to `wit_bindgen::rt`.
207    #[cfg_attr(feature = "clap", arg(long))]
208    pub runtime_path: Option<String>,
209
210    /// The optional path to the bitflags crate to use.
211    ///
212    /// This defaults to `wit_bindgen::bitflags`.
213    #[cfg_attr(feature = "clap", arg(long))]
214    pub bitflags_path: Option<String>,
215
216    /// Additional derive attributes to add to generated types. If using in a CLI, this flag can be
217    /// specified multiple times to add multiple attributes.
218    ///
219    /// These derive attributes will be added to any generated structs or enums
220    #[cfg_attr(feature = "clap", arg(long = "additional_derive_attribute", short = 'd', default_values_t = Vec::<String>::new()))]
221    pub additional_derive_attributes: Vec<String>,
222
223    /// Remapping of interface names to rust module names.
224    #[cfg_attr(feature = "clap", arg(long, value_parser = parse_with, default_value = ""))]
225    pub with: HashMap<String, String>,
226}
227
228impl Opts {
229    pub fn build(self) -> Box<dyn WorldGenerator> {
230        let mut r = MoonBit::new();
231        r.skip = self.skip.iter().cloned().collect();
232        r.opts = self;
233        Box::new(r)
234    }
235}
236
237impl MoonBit {
238    fn new() -> MoonBit {
239        MoonBit::default()
240    }
241
242    fn interface<'a>(
243        &'a mut self,
244        identifier: Identifier<'a>,
245        wasm_import_module: Option<&'a str>,
246        resolve: &'a Resolve,
247        in_import: bool,
248    ) -> interface::InterfaceGenerator<'a> {
249        let mut sizes = SizeAlign::default();
250        sizes.fill(resolve);
251
252        interface::InterfaceGenerator {
253            identifier,
254            wasm_import_module,
255            src: Source::default(),
256            in_import,
257            gen: self,
258            sizes,
259            resolve,
260            return_pointer_area_size: 0,
261            return_pointer_area_align: 0,
262        }
263    }
264
265    fn emit_modules(&mut self, modules: Vec<(String, Vec<ModuleName>)>, in_import: bool) {
266        #[derive(Default)]
267        struct Module {
268            submodules: BTreeMap<ModuleName, Module>,
269            contents: Vec<String>,
270        }
271        let mut map = Module::default();
272        for (module, path) in modules {
273            let mut cur = &mut map;
274            for name in path.iter() {
275                cur = cur
276                    .submodules
277                    .entry(name.clone())
278                    .or_insert(Module::default());
279            }
280            cur.contents.push(module);
281        }
282        let inits = emit(&mut self.src, map, in_import);
283        if in_import {
284            for (snake, init) in inits {
285                uwriteln!(self.src, "pub let {}: {} = {init}\n", snake.snake, snake.qual);
286            }
287        }
288        fn emit(me: &mut Source, module: Module, in_import: bool) -> Vec<(ModuleName, String)> {
289            let mut rv = Vec::new();
290            for (name, submodule) in module.submodules {
291                if in_import {
292                    if submodule.submodules.is_empty() {
293                        uwriteln!(me, "pub(readonly) type {} Unit", name.qual);
294                    } else {
295                        uwriteln!(me, "pub(readonly) struct {} {{", name.qual);
296                        for (name, _) in submodule.submodules.iter() {
297                            uwriteln!(me, "{}: {}", name.snake, name.qual);
298                        }
299                        uwriteln!(me, "}}");
300                    }
301                    uwriteln!(me, "");
302                }
303
304                let sub_inits = emit(me, submodule, in_import);
305                if in_import {
306                    let init = if sub_inits.is_empty() {
307                        format!("{}(())", name.qual)
308                    } else {
309                        let init = sub_inits
310                            .into_iter()
311                            .map(|(name, init)| format!("{}: {init}", name.snake))
312                            .collect::<Vec<_>>()
313                            .join(", ");
314                        format!("{{ {init} }}")
315                    };
316                    rv.push((name, init));
317                }
318            }
319            for submodule in module.contents {
320                uwriteln!(me, "{submodule}");
321            }
322            rv
323        }
324    }
325
326    fn runtime_path(&self) -> &str {
327        self.opts
328            .runtime_path
329            .as_deref()
330            .unwrap_or("wit_bindgen::rt")
331    }
332
333    fn bitflags_path(&self) -> &str {
334        self.opts
335            .bitflags_path
336            .as_deref()
337            .unwrap_or("wit_bindgen::bitflags")
338    }
339
340    fn lookup_export(&self, key: &ExportKey) -> Result<String> {
341        if let Some(key) = self.opts.exports.get(key) {
342            return Ok(key.clone());
343        }
344        if self.opts.stubs {
345            return Ok("Stub".to_owned());
346        }
347        let key = match key {
348            ExportKey::World => "world".to_owned(),
349            ExportKey::Name(name) => format!("\"{name}\""),
350        };
351        if self.opts.exports.is_empty() {
352            bail!("no `exports` map provided in configuration - provide an `exports` map a key `{key}`")
353        }
354        bail!("expected `exports` map to contain key `{key}`")
355    }
356
357    fn name_interface(
358        &mut self,
359        resolve: &Resolve,
360        id: InterfaceId,
361        name: &WorldKey,
362        is_export: bool,
363    ) -> bool {
364        let with_name = resolve.name_world_key(name);
365        let entry = if let Some(remapped_path) = self.opts.with.get(&with_name) {
366            let name = format!("__with_name{}", self.with_name_counter);
367            self.with_name_counter += 1;
368            uwriteln!(self.src, "use {remapped_path} as {name};");
369            InterfaceName {
370                remapped: true,
371                path: name,
372            }
373        } else {
374            let path = compute_module_path(name, resolve, is_export)
375                .into_iter()
376                .map(|n| n.snake)
377                .collect::<Vec<_>>()
378                .join("::");
379
380            InterfaceName {
381                remapped: false,
382                path,
383            }
384        };
385
386        let remapped = entry.remapped;
387        self.interface_names.insert(id, entry);
388
389        remapped
390    }
391}
392
393/// If the package `id` is the only package with its namespace/name combo
394/// then pass through the name unmodified. If, however, there are multiple
395/// versions of this package then the package module is going to get version
396/// information.
397fn name_package_module(resolve: &Resolve, id: PackageId) -> String {
398    let pkg = &resolve.packages[id];
399    let versions_with_same_name = resolve
400        .packages
401        .iter()
402        .filter_map(|(_, p)| {
403            if p.name.namespace == pkg.name.namespace && p.name.name == pkg.name.name {
404                Some(&p.name.version)
405            } else {
406                None
407            }
408        })
409        .collect::<Vec<_>>();
410    let base = pkg.name.name.to_snake_case();
411    if versions_with_same_name.len() == 1 {
412        return base;
413    }
414
415    let version = match &pkg.name.version {
416        Some(version) => version,
417        // If this package didn't have a version then don't mangle its name
418        // and other packages with the same name but with versions present
419        // will have their names mangled.
420        None => return base,
421    };
422
423    // Here there's multiple packages with the same name that differ only in
424    // version, so the version needs to be mangled into the Rust module name
425    // that we're generating. This in theory could look at all of
426    // `versions_with_same_name` and produce a minimal diff, e.g. for 0.1.0
427    // and 0.2.0 this could generate "foo1" and "foo2", but for now
428    // a simpler path is chosen to generate "foo0_1_0" and "foo0_2_0".
429    let version = version
430        .to_string()
431        .replace('.', "_")
432        .replace('-', "_")
433        .replace('+', "_")
434        .to_snake_case();
435    format!("{base}{version}")
436}
437
438impl WorldGenerator for MoonBit {
439    fn preprocess(&mut self, resolve: &Resolve, _world: WorldId) {
440        wit_bindgen_core::generated_preamble(&mut self.src, env!("CARGO_PKG_VERSION"));
441        self.types.analyze(resolve);
442    }
443
444    fn import_interface(
445        &mut self,
446        resolve: &Resolve,
447        name: &WorldKey,
448        id: InterfaceId,
449        _files: &mut Files,
450    ) {
451        let wasm_import_module = resolve.name_world_key(name);
452        let mut gen = self.interface(
453            Identifier::Interface(id, name),
454            Some(&wasm_import_module),
455            resolve,
456            true,
457        );
458        let module_path = gen.start_append_submodule(name);
459        if gen.gen.name_interface(resolve, id, name, false) {
460            return;
461        }
462        gen.types(id);
463
464        gen.generate_imports(resolve.interfaces[id].functions.values(), module_path.last());
465
466        gen.finish_append_submodule(module_path);
467    }
468
469    fn import_funcs(
470        &mut self,
471        resolve: &Resolve,
472        world: WorldId,
473        funcs: &[(&str, &Function)],
474        _files: &mut Files,
475    ) {
476        self.import_funcs_called = true;
477
478        let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true);
479
480        gen.generate_imports(funcs.iter().map(|(_, func)| *func), None);
481
482        let src = gen.finish();
483        self.src.push_str(&src);
484    }
485
486    fn export_interface(
487        &mut self,
488        resolve: &Resolve,
489        name: &WorldKey,
490        id: InterfaceId,
491        _files: &mut Files,
492    ) -> Result<()> {
493        let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false);
494        let module_path = gen.start_append_submodule(name);
495        if gen.gen.name_interface(resolve, id, name, true) {
496            return Ok(());
497        }
498        gen.types(id);
499        gen.generate_exports(resolve.interfaces[id].functions.values(), module_path.last())?;
500        gen.finish_append_submodule(module_path);
501        Ok(())
502    }
503
504    fn export_funcs(
505        &mut self,
506        resolve: &Resolve,
507        world: WorldId,
508        funcs: &[(&str, &Function)],
509        _files: &mut Files,
510    ) -> Result<()> {
511        let mut gen = self.interface(Identifier::World(world), None, resolve, false);
512        gen.generate_exports(funcs.iter().map(|f| f.1), None)?;
513        let src = gen.finish();
514        self.src.push_str(&src);
515        Ok(())
516    }
517
518    fn import_types(
519        &mut self,
520        resolve: &Resolve,
521        world: WorldId,
522        types: &[(&str, TypeId)],
523        _files: &mut Files,
524    ) {
525        let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true);
526        for (name, ty) in types {
527            gen.define_type(name, *ty);
528        }
529        let src = gen.finish();
530        self.src.push_str(&src);
531    }
532
533    fn finish_imports(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) {
534        if !self.import_funcs_called {
535            // We call `import_funcs` even if the world doesn't import any
536            // functions since one of the side effects of that method is to
537            // generate `struct`s for any imported resources.
538            self.import_funcs(resolve, world, &[], files);
539        }
540    }
541
542    fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) {
543        let name = &resolve.worlds[world].name;
544
545        let imports = mem::take(&mut self.import_modules);
546        self.emit_modules(imports, true);
547        let exports = mem::take(&mut self.export_modules);
548        self.emit_modules(exports, false);
549
550        if self.opts.stubs {
551            self.src.push_str("\n#[derive(Debug)]\npub struct Stub;\n");
552            let world_id = world;
553            let world = &resolve.worlds[world];
554            let mut funcs = Vec::new();
555            for (name, export) in world.exports.iter() {
556                let (pkg, name) = match name {
557                    WorldKey::Name(name) => (None, name),
558                    WorldKey::Interface(id) => {
559                        let interface = &resolve.interfaces[*id];
560                        (
561                            Some(interface.package.unwrap()),
562                            interface.name.as_ref().unwrap(),
563                        )
564                    }
565                };
566                match export {
567                    WorldItem::Function(func) => {
568                        funcs.push(func);
569                    }
570                    WorldItem::Interface(id) => {
571                        for (resource, funcs) in
572                            group_by_resource(resolve.interfaces[*id].functions.values())
573                        {
574                            let mut gen =
575                                self.interface(Identifier::World(world_id), None, resolve, false);
576                            let pkg = pkg.map(|pid| {
577                                let namespace = resolve.packages[pid].name.namespace.clone();
578                                let package_module = name_package_module(resolve, pid);
579                                (namespace, package_module)
580                            });
581                            gen.generate_stub(resource, pkg, name, true, &funcs);
582                            let stub = gen.finish();
583                            self.src.push_str(&stub);
584                        }
585                    }
586                    WorldItem::Type(_) => unreachable!(),
587                }
588            }
589
590            for (resource, funcs) in group_by_resource(funcs.into_iter()) {
591                let mut gen = self.interface(Identifier::World(world_id), None, resolve, false);
592                gen.generate_stub(resource, None, &world.name, false, &funcs);
593                let stub = gen.finish();
594                self.src.push_str(&stub);
595            }
596        }
597
598        uwriteln!(self.src, "struct GuestImpl {{");
599        for (trait_name, field) in self.export_traits.iter() {
600            uwriteln!(self.src, "mut {field}: Option[{trait_name}]");
601        }
602        uwriteln!(self.src, "}} derive(Default)");
603        uwriteln!(self.src, "");
604        uwriteln!(self.src, "let guest_impl: GuestImpl = GuestImpl::default()");
605        uwriteln!(self.src, "");
606        self.src.push_str("pub fn init_guest[T: ");
607        self.src.push_str(
608            &self
609                .export_traits
610                .keys()
611                .map(String::as_str)
612                .collect::<Vec<_>>()
613                .join(" + "),
614        );
615        uwriteln!(self.src, "](guest: T) {{");
616        for (trait_name, field) in self.export_traits.iter() {
617            uwriteln!(self.src, "guest_impl.{field} = Some(guest as {trait_name})");
618        }
619        uwriteln!(self.src, "}}\n");
620
621        let mut builtins = self.imported_builtins.iter().collect::<Vec<_>>();
622        builtins.sort();
623        for (i, builtin) in builtins.into_iter().enumerate() {
624            if i > 0 {
625                uwriteln!(self.src, "");
626            }
627            let def = match *builtin {
628                "_rael_malloc" => "(size: Int) -> Int = \"$rael.malloc\"",
629                "_rael_free" => "(ptr: Int) = \"$rael.free\"",
630                "_rael_memory_copy" => "(dst: Int, src: Int, len: Int) = \"$rael.memory_copy\"",
631                "_rael_load_i32" => "(ptr: Int) -> Int = \"$rael.load_i32\"",
632                "_rael_load_i64" => "(ptr: Int) -> Int64 = \"$rael.load_i64\"",
633                "_rael_bytes_data" => "(b: Bytes) -> Int = \"$rael.bytes_data\"",
634                "_mbt_string_data" => "(s: String) -> Int = \"$moonbit.string_data\"",
635                "_mbt_unsafe_make_string" => {
636                    "(len: Int, val: Int) -> String = \"$moonbit.unsafe_make_string\""
637                },
638                _ => unreachable!()
639            };
640            uwriteln!(self.src, "fn {builtin}{def}");
641        }
642
643        let src = mem::take(&mut self.src);
644        let module_name = name.to_snake_case();
645        files.push(&format!("{module_name}.mbt"), src.as_bytes());
646    }
647}
648
649fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> Vec<ModuleName> {
650    let mut path = Vec::new();
651    if is_export {
652        path.push("exports".to_string());
653    }
654    match name {
655        WorldKey::Name(name) => {
656            path.push(name.to_snake_case());
657        }
658        WorldKey::Interface(id) => {
659            let iface = &resolve.interfaces[*id];
660            let pkg = iface.package.unwrap();
661            let pkgname = resolve.packages[pkg].name.clone();
662            path.push(pkgname.namespace.to_snake_case());
663            path.push(name_package_module(resolve, pkg));
664            path.push(iface.name.as_ref().unwrap().to_snake_case());
665        }
666    }
667    let mut iter = path.into_iter();
668    let first = ModuleName::root(iter.next().unwrap());
669    let (mut rv, last) = iter.fold((vec![], first), |(mut rv, prev), name| {
670        let name = prev.child(name);
671        rv.push(prev);
672        (rv, name)
673    });
674    rv.push(last);
675    rv
676}
677
678enum Identifier<'a> {
679    #[allow(dead_code)]
680    World(WorldId),
681    Interface(InterfaceId, &'a WorldKey),
682}
683
684fn group_by_resource<'a>(
685    funcs: impl Iterator<Item = &'a Function>,
686) -> BTreeMap<Option<TypeId>, Vec<&'a Function>> {
687    let mut by_resource = BTreeMap::<_, Vec<_>>::new();
688    for func in funcs {
689        match &func.kind {
690            FunctionKind::Freestanding => by_resource.entry(None).or_default().push(func),
691            FunctionKind::Method(ty) | FunctionKind::Static(ty) | FunctionKind::Constructor(ty) => {
692                by_resource.entry(Some(*ty)).or_default().push(func);
693            }
694        }
695    }
696    by_resource
697}
698
699#[derive(Debug, Copy, Clone, PartialEq)]
700enum TypeMode {
701    Owned,
702    AllBorrowed(&'static str),
703    HandlesBorrowed(&'static str),
704}
705
706#[derive(Default, Debug, Clone, Copy)]
707pub enum Ownership {
708    /// Generated types will be composed entirely of owning fields, regardless
709    /// of whether they are used as parameters to imports or not.
710    #[default]
711    Owning,
712
713    /// Generated types used as parameters to imports will be "deeply
714    /// borrowing", i.e. contain references rather than owned values when
715    /// applicable.
716    Borrowing {
717        /// Whether or not to generate "duplicate" type definitions for a single
718        /// WIT type if necessary, for example if it's used as both an import
719        /// and an export, or if it's used both as a parameter to an import and
720        /// a return value from an import.
721        duplicate_if_necessary: bool,
722    },
723}
724
725impl FromStr for Ownership {
726    type Err = String;
727
728    fn from_str(s: &str) -> Result<Self, Self::Err> {
729        match s {
730            "owning" => Ok(Self::Owning),
731            "borrowing" => Ok(Self::Borrowing {
732                duplicate_if_necessary: false,
733            }),
734            "borrowing-duplicate-if-necessary" => Ok(Self::Borrowing {
735                duplicate_if_necessary: true,
736            }),
737            _ => Err(format!(
738                "unrecognized ownership: `{s}`; \
739                 expected `owning`, `borrowing`, or `borrowing-duplicate-if-necessary`"
740            )),
741        }
742    }
743}
744
745impl fmt::Display for Ownership {
746    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
747        f.write_str(match self {
748            Ownership::Owning => "owning",
749            Ownership::Borrowing {
750                duplicate_if_necessary: false,
751            } => "borrowing",
752            Ownership::Borrowing {
753                duplicate_if_necessary: true,
754            } => "borrowing-duplicate-if-necessary",
755        })
756    }
757}
758
759#[derive(Default)]
760struct FnSig {
761    async_: bool,
762    unsafe_: bool,
763    private: bool,
764    use_item_name: bool,
765    generics: Option<String>,
766    self_arg: Option<String>,
767    self_is_first_param: bool,
768    is_trait: bool,
769}
770
771pub fn to_mbt_ident(name: &str) -> String {
772    match name {
773        // Escape MoonBit keywords.
774        // Source: https://www.moonbitlang.com/docs/syntax
775        "as" => "as_".into(),
776        "break" => "break_".into(),
777        "continue" => "continue_".into(),
778        "else" => "else_".into(),
779        "enum" => "enum_".into(),
780        "false" => "false_".into(),
781        "fn" => "fn_".into(),
782        "if" => "if_".into(),
783        "loop" => "loop_".into(),
784        "match" => "match_".into(),
785        "mut" => "mut_".into(),
786        "pub" => "pub_".into(),
787        "return" => "return_".into(),
788        "self" => "self_".into(),
789        "struct" => "struct_".into(),
790        "trait" => "trait_".into(),
791        "true" => "true_".into(),
792        "type" => "type_".into(),
793        "while" => "while_".into(),
794        "priv" => "priv_".into(),
795        s => s.to_snake_case(),
796    }
797}
798
799fn to_upper_camel_case(name: &str) -> String {
800    match name {
801        // The name "Guest" is reserved for traits generated by exported
802        // interfaces, so remap types defined in wit to something else.
803        "guest" => "Guest_".to_string(),
804        s => s.to_upper_camel_case(),
805    }
806}
807
808fn wasm_type(ty: WasmType) -> &'static str {
809    match ty {
810        WasmType::I32 => "Int",
811        WasmType::I64 => "Int64",
812        WasmType::F32 => "Float",
813        WasmType::F64 => "Float64",
814    }
815}
816
817fn int_repr(repr: Int) -> &'static str {
818    match repr {
819        Int::U8 => "u8",
820        Int::U16 => "u16",
821        Int::U32 => "u32",
822        Int::U64 => "u64",
823    }
824}
825
826fn bitcast(casts: &[Bitcast], operands: &[String], results: &mut Vec<String>) {
827    for (cast, operand) in casts.iter().zip(operands) {
828        results.push(match cast {
829            Bitcast::None => operand.clone(),
830            Bitcast::I32ToI64 => format!("i64::from({})", operand),
831            Bitcast::F32ToI32 => format!("({}).to_bits() as i32", operand),
832            Bitcast::F64ToI64 => format!("({}).to_bits() as i64", operand),
833            Bitcast::I64ToI32 => format!("{} as i32", operand),
834            Bitcast::I32ToF32 => format!("f32::from_bits({} as u32)", operand),
835            Bitcast::I64ToF64 => format!("f64::from_bits({} as u64)", operand),
836            Bitcast::F32ToI64 => format!("i64::from(({}).to_bits())", operand),
837            Bitcast::I64ToF32 => format!("f32::from_bits({} as u32)", operand),
838        });
839    }
840}
841
842enum MbtFlagsRepr {
843    U8,
844    U16,
845    U32,
846    U64,
847    U128,
848}
849
850impl MbtFlagsRepr {
851    fn new(f: &Flags) -> MbtFlagsRepr {
852        match f.repr() {
853            FlagsRepr::U8 => MbtFlagsRepr::U8,
854            FlagsRepr::U16 => MbtFlagsRepr::U16,
855            FlagsRepr::U32(1) => MbtFlagsRepr::U32,
856            FlagsRepr::U32(2) => MbtFlagsRepr::U64,
857            FlagsRepr::U32(3 | 4) => MbtFlagsRepr::U128,
858            FlagsRepr::U32(n) => panic!("unsupported number of flags: {}", n * 32),
859        }
860    }
861}
862
863impl fmt::Display for MbtFlagsRepr {
864    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
865        match self {
866            MbtFlagsRepr::U8 => "u8".fmt(f),
867            MbtFlagsRepr::U16 => "u16".fmt(f),
868            MbtFlagsRepr::U32 => "u32".fmt(f),
869            MbtFlagsRepr::U64 => "u64".fmt(f),
870            MbtFlagsRepr::U128 => "u128".fmt(f),
871        }
872    }
873}