wasmcloud-provider-wit-bindgen-macro 0.1.1

Internal-use bindgen macro for binary-based wasmCloud capability providers written in Rust
Documentation
//! wRPC related functionality
//!

use anyhow::{Context, Result};
use wit_parser::Resolve;

/// Human readable name of a [`wit_parser::WorldKey`] which includes interface ID if necessary
/// see: https://docs.rs/wit-parser/latest/wit_parser/struct.Resolve.html#method.name_world_key
type WorldKeyName = String;

/// WIT function name
type WitFunctionName = String;

/// A struct to hold information on exports that should be use with wRPC
/// this information is normally used to construct macro output
pub(crate) struct WrpcExport {
    pub(crate) wit_ns: String,
    pub(crate) wit_pkg: String,
    pub(crate) wit_iface: String,
    pub(crate) wit_iface_fn: String,
    /// Information generated by wrpc_types
    pub(crate) types: (WorldKeyName, WitFunctionName, wrpc_types::DynamicFunction),
}

/// Generate a list of [`WrpcExport`] which will be used to generate incoming NATS RPC subjects
/// on which the provider should listen.
pub(crate) fn generate_wrpc_nats_subject_to_fn_mapping(
    resolve: &Resolve,
) -> Result<Vec<WrpcExport>> {
    let mut subjects = Vec::new();

    for (_, world) in resolve.worlds.iter() {
        // Generate a lookup of all dynamic functions for this world export
        let mut wrpc_type_lookup = wrpc_types::function_exports(resolve, world.exports.iter());

        // Generate the mapping from exports of the WIT world (roughly, visiting all the exported functions)
        for (world_item, _) in world.exports.iter() {
            match world_item {
                wit_parser::WorldKey::Name(_) => continue,
                wit_parser::WorldKey::Interface(iface_id) => {
                    let iface = resolve.interfaces.get(*iface_id).with_context(|| {
                        format!("unexpectedly missing iface with ID [{iface_id:?}]")
                    })?;

                    let iface_name = iface.name.as_ref().with_context(|| {
                        format!(
                            "unexpectedly un-named iface ID [{iface_id:?}] in world [{}]",
                            &world.name,
                        )
                    })?;

                    let iface_pkg = resolve
                        .packages
                        .get(iface.package.with_context(|| {
                            format!("unexpectedly missing package for iface [{iface_name}] ")
                        })?)
                        .with_context(|| {
                            "missing top level referenced package for iface [{iface_name}]"
                        })?;
                    let ns = &iface_pkg.name.namespace;
                    let pkg_name = &iface_pkg.name.name;

                    for (fn_name, _) in iface.functions.iter() {
                        let world_key_name = resolve.name_world_key(world_item);

                        // Look up and extract the dynamic function that matches this world key and function
                        let dynamic_fn = wrpc_type_lookup
                            .get_mut(&world_key_name)
                            .and_then(|lookup| lookup.remove(fn_name))
                            .with_context(|| {
                                format!("failed to look up dynamic [{world_key_name}.{fn_name}]")
                            })?;

                        subjects.push(WrpcExport {
                            wit_ns: ns.into(),
                            wit_pkg: pkg_name.into(),
                            wit_iface: iface_name.into(),
                            wit_iface_fn: fn_name.into(),
                            types: (world_key_name, fn_name.into(), dynamic_fn),
                        });
                    }
                }
            }
        }
    }
    Ok(subjects)
}