Add only_interfaces and with to the bindgen! macro. (#6160)

* Add `only_interfaces` and `with` to the `bindgen!` macro.

* Add a version of the empty_error test for `only_interfaces` and `with`

* Review feedback

* Add docs
This commit is contained in:
Trevor Elliott
2023-04-10 16:28:52 -07:00
committed by GitHub
parent 4053ae9e08
commit 85f0c68008
5 changed files with 257 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
use proc_macro2::{Span, TokenStream};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
@@ -77,6 +78,8 @@ impl Parse for Config {
Opt::Async(val) => opts.async_ = val,
Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
Opt::DuplicateIfNecessary(val) => opts.duplicate_if_necessary = val,
Opt::OnlyInterfaces(val) => opts.only_interfaces = val,
Opt::With(val) => opts.with.extend(val),
}
}
} else {
@@ -133,6 +136,8 @@ mod kw {
syn::custom_keyword!(trappable_error_type);
syn::custom_keyword!(world);
syn::custom_keyword!(duplicate_if_necessary);
syn::custom_keyword!(only_interfaces);
syn::custom_keyword!(with);
}
enum Opt {
@@ -143,6 +148,8 @@ enum Opt {
Async(bool),
TrappableErrorType(Vec<TrappableError>),
DuplicateIfNecessary(bool),
OnlyInterfaces(bool),
With(HashMap<String, String>),
}
impl Parse for Opt {
@@ -191,6 +198,18 @@ impl Parse for Opt {
})
.collect(),
))
} else if l.peek(kw::only_interfaces) {
input.parse::<kw::only_interfaces>()?;
input.parse::<Token![:]>()?;
Ok(Opt::OnlyInterfaces(input.parse::<syn::LitBool>()?.value))
} else if l.peek(kw::with) {
input.parse::<kw::with>()?;
input.parse::<Token![:]>()?;
let contents;
let _lbrace = braced!(contents in input);
let fields: Punctuated<(String, String), Token![,]> =
contents.parse_terminated(with_field_parse)?;
Ok(Opt::With(HashMap::from_iter(fields.into_iter())))
} else {
Err(l.error())
}
@@ -219,3 +238,46 @@ fn trappable_error_field_parse(input: ParseStream<'_>) -> Result<(String, String
let rust_type = input.parse::<Ident>()?.to_string();
Ok((interface, type_, rust_type))
}
fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> {
let interface = input.parse::<syn::LitStr>()?.value();
input.parse::<Token![:]>()?;
let start = input.span();
let path = input.parse::<syn::Path>()?;
// It's not possible for the segments of a path to be empty
let span = start
.join(path.segments.last().unwrap().ident.span())
.unwrap_or(start);
let mut buf = String::new();
let append = |buf: &mut String, segment: syn::PathSegment| -> Result<()> {
if segment.arguments != syn::PathArguments::None {
return Err(Error::new(
span,
"Module path must not contain angles or parens",
));
}
buf.push_str(&segment.ident.to_string());
Ok(())
};
if path.leading_colon.is_some() {
buf.push_str("::");
}
let mut segments = path.segments.into_iter();
if let Some(segment) = segments.next() {
append(&mut buf, segment)?;
}
for segment in segments {
buf.push_str("::");
append(&mut buf, segment)?;
}
Ok((interface, buf))
}

View File

@@ -21,9 +21,17 @@ macro_rules! gentest {
duplicate_if_necessary: true,
});
}
mod interfaces_only {
wasmtime::component::bindgen!({
path: $path,
world: $name,
only_interfaces: true,
});
}
}
// ...
};
}
component_macro_test_helpers::foreach!(gentest);