Available on crate feature runtime and crate feature component-model and docsrs only.
Expand description

Example of generating bindings for imported resources in a world.

Notable parts of this example are:

  • Imported resources from the host are represented as traits, in this case HostLogger.
  • The per-interface Host trait still exists but has a supertrait of HostLogger.
  • Resources are represented as Resource<T> and it’s recommended to specify a with key to indicate what host type you’d like to use for each resource.
  • A ResourceTable can be used to manage resources when working with guests.
use wasmtime::Result;
use wasmtime::component::{bindgen, ResourceTable, Resource};
use example::imported_resources::logging::{Level, Host, HostLogger};

bindgen!({
    inline: r#"
        package example:imported-resources;

        interface logging {
            enum level {
                debug,
                info,
                warn,
                error,
            }

            resource logger {
                constructor(max-level: level);

                get-max-level: func() -> level;
                set-max-level: func(level: level);

                log: func(level: level, msg: string);
            }
        }

        world import-some-resources {
            import logging;
        }
    "#,

    with: {
        // Specify that our host resource is going to point to the `MyLogger`
        // which is defined just below this macro.
        "example:imported-resources/logging/logger": MyLogger,
    },

    // Interactions with `ResourceTable` can possibly trap so enable the ability
    // to return traps from generated functions.
    trappable_imports: true,
});

/// A sample host-defined type which contains arbitrary host-defined data.
///
/// In this case this is relatively simple but there's no restrictions on what
/// this type can hold other than that it must be `'static + Send`.
pub struct MyLogger {
    pub max_level: example::imported_resources::logging::Level,
}

#[derive(Default)]
struct MyState {
    // Manages the mapping of `MyLogger` structures to `Resource<MyLogger>`.
    table: ResourceTable,
}

// There are no free-functions on `interface logging`, so this is an empty
// impl.
impl Host for MyState {}

// This separate `HostLogger` trait serves to act as a namespace for just
// the `logger`-related resource methods.
impl HostLogger for MyState {
    // A `constructor` in WIT maps to a `new` function in Rust.
    fn new(&mut self, max_level: Level) -> Result<Resource<MyLogger>> {
        let id = self.table.push(MyLogger { max_level })?;
        Ok(id)
    }

    fn get_max_level(&mut self, logger: Resource<MyLogger>) -> Result<Level> {
        debug_assert!(!logger.owned());
        let logger = self.table.get(&logger)?;
        Ok(logger.max_level)
    }

    fn set_max_level(&mut self, logger: Resource<MyLogger>, level: Level) -> Result<()> {
        debug_assert!(!logger.owned());
        let logger = self.table.get_mut(&logger)?;
        logger.max_level = level;
        Ok(())
    }

    fn log(&mut self, logger: Resource<MyLogger>, level: Level, msg: String) -> Result<()> {
        debug_assert!(!logger.owned());
        let logger = self.table.get_mut(&logger)?;
        if (level as u32) <= (logger.max_level as u32) {
            println!("{msg}");
        }
        Ok(())
    }

    fn drop(&mut self, logger: Resource<MyLogger>) -> Result<()> {
        debug_assert!(logger.owned());
        let _logger: MyLogger = self.table.delete(logger)?;
        // ... custom destruction logic here if necessary, otherwise
        // a `Drop for MyLogger` would also work.
        Ok(())
    }
}

Modules§

Structs§

  • Auto-generated bindings for an instance a component which implements the world import-some-resources.
  • Auto-generated bindings for index of the exports of import-some-resources.
  • Auto-generated bindings for a pre-instantiated version of a component which implements the world import-some-resources.
  • A sample host-defined type which contains arbitrary host-defined data.