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}