cargo_component_macro/
lib.rs

1//! A proc-macro crate for generating bindings with `cargo-component`.
2
3#![deny(missing_docs)]
4
5use proc_macro2::{Span, TokenStream};
6use quote::quote;
7use std::path::{Path, PathBuf};
8use syn::{Error, Result};
9
10fn bindings_source_path() -> Result<PathBuf> {
11    let path = Path::new(env!("CARGO_TARGET_DIR"))
12        .join("bindings")
13        .join(
14            std::env::var("CARGO_PKG_NAME")
15                .expect("failed to get `CARGO_PKG_NAME` environment variable"),
16        )
17        .join("bindings.rs");
18
19    if !path.is_file() {
20        return Err(Error::new(
21            Span::call_site(),
22            format!(
23                "bindings file `{path}` does not exist\n\n\
24                 did you forget to run `cargo component build`? (https://github.com/bytecodealliance/cargo-component)",
25                path = path.display()
26            ),
27        ));
28    }
29
30    Ok(path)
31}
32
33fn generate_bindings(input: proc_macro::TokenStream) -> Result<TokenStream> {
34    if !input.is_empty() {
35        return Err(Error::new(
36            Span::call_site(),
37            "the `generate!` macro does not take any arguments",
38        ));
39    }
40
41    let path = bindings_source_path()?;
42    let path = path.to_str().expect("bindings path is not valid UTF-8");
43
44    Ok(quote! {
45        /// Generated bindings module for this component.
46        #[allow(dead_code)]
47        pub(crate) mod bindings {
48            include!(#path);
49        }
50    })
51}
52
53/// Used to generate bindings for a WebAssembly component.
54///
55/// By default, all world exports are expected to be implemented
56/// on a type named `Component` where the `bindings!` macro
57/// is invoked.
58///
59/// Additionally, all resource exports are expected to be
60/// implemented on a type named `<ResourceName>`.
61///
62/// For example, a resource named `file` would be implemented
63/// on a type named `File` in the same scope as the `generate!`
64/// macro invocation.
65///
66/// # Settings
67///
68/// Use the `package.metadata.component.bindings` section in
69/// `Cargo.toml` to configure bindings generation.
70///
71/// The available settings are:
72///
73/// - `implementor`: The name of the type to implement world exports on.
74/// - `resources`: A map of resource names to resource implementor types.
75/// - `ownership`: The ownership model to use for resources.
76/// - `derives`: Additional derive macro attributes to add to generated types.
77///
78/// # Examples
79///
80/// Specifying a custom implementor type named `MyComponent`:
81///
82/// ```toml
83/// [package.metadata.component.bindings]
84/// implementor = "MyComponent"
85/// ```
86///
87/// Specifying a custom resource implementor type named `MyResource`:
88///
89/// ```toml
90/// [package.metadata.component.bindings.resources]
91/// "my:package/iface/res" = "MyResource"
92/// ```
93///
94/// Specifying the `borrowing-duplicate-if-necessary` ownership model:
95///
96/// ```toml
97/// [package.metadata.component.bindings]
98/// ownership = "borrowing-duplicate-if-necessary"
99/// ````
100#[proc_macro]
101pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
102    generate_bindings(input)
103        .unwrap_or_else(Error::into_compile_error)
104        .into()
105}