Allow inline source and the path attribute in the host bindgen! macro (#6226)

* Allow inline source and the `path` attribute in the host bindgen! macro

* Report an error if `world` is passed with `interfaces`
This commit is contained in:
Trevor Elliott
2023-04-17 16:51:52 -07:00
committed by GitHub
parent 9ee613a0b7
commit d4b771e5a3

View File

@@ -38,17 +38,13 @@ pub fn expand(input: &Config) -> Result<TokenStream> {
Ok(contents) Ok(contents)
} }
enum Source {
Path(String),
Inline(String),
}
impl Parse for Config { impl Parse for Config {
fn parse(input: ParseStream<'_>) -> Result<Self> { fn parse(input: ParseStream<'_>) -> Result<Self> {
let call_site = Span::call_site(); let call_site = Span::call_site();
let mut opts = Opts::default(); let mut opts = Opts::default();
let mut source = None;
let mut world = None; let mut world = None;
let mut inline = None;
let mut path = None;
if input.peek(token::Brace) { if input.peek(token::Brace) {
let content; let content;
@@ -57,10 +53,10 @@ impl Parse for Config {
for field in fields.into_pairs() { for field in fields.into_pairs() {
match field.into_value() { match field.into_value() {
Opt::Path(s) => { Opt::Path(s) => {
if source.is_some() { if path.is_some() {
return Err(Error::new(s.span(), "cannot specify second source")); return Err(Error::new(s.span(), "cannot specify second path"));
} }
source = Some(Source::Path(s.value())); path = Some(s.value());
} }
Opt::World(s) => { Opt::World(s) => {
if world.is_some() { if world.is_some() {
@@ -69,23 +65,29 @@ impl Parse for Config {
world = Some(s.value()); world = Some(s.value());
} }
Opt::Inline(s) => { Opt::Inline(s) => {
if source.is_some() { if inline.is_some() {
return Err(Error::new(s.span(), "cannot specify second source")); return Err(Error::new(s.span(), "cannot specify second source"));
} }
source = Some(Source::Inline(s.value())); inline = Some(s.value());
} }
Opt::Tracing(val) => opts.tracing = val, Opt::Tracing(val) => opts.tracing = val,
Opt::Async(val) => opts.async_ = val, Opt::Async(val) => opts.async_ = val,
Opt::TrappableErrorType(val) => opts.trappable_error_type = val, Opt::TrappableErrorType(val) => opts.trappable_error_type = val,
Opt::DuplicateIfNecessary(val) => opts.duplicate_if_necessary = val, Opt::DuplicateIfNecessary(val) => opts.duplicate_if_necessary = val,
Opt::Interfaces(s) => { Opt::Interfaces(s) => {
if source.is_some() { if inline.is_some() {
return Err(Error::new(s.span(), "cannot specify a second source")); return Err(Error::new(s.span(), "cannot specify a second source"));
} }
source = Some(Source::Inline(format!( inline = Some(format!("default world interfaces {{ {} }}", s.value()));
"default world interfaces {{ {} }}",
s.value() if world.is_some() {
))); return Err(Error::new(
s.span(),
"cannot specify a world with `interfaces`",
));
}
world = Some("macro-input.interfaces".to_string());
opts.only_interfaces = true; opts.only_interfaces = true;
} }
Opt::With(val) => opts.with.extend(val), Opt::With(val) => opts.with.extend(val),
@@ -94,11 +96,12 @@ impl Parse for Config {
} else { } else {
world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value()); world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
if input.parse::<Option<syn::token::In>>()?.is_some() { if input.parse::<Option<syn::token::In>>()?.is_some() {
source = Some(Source::Path(input.parse::<syn::LitStr>()?.value())); path = Some(input.parse::<syn::LitStr>()?.value());
} }
} }
let (resolve, pkg, files) = let (resolve, pkg, files) = parse_source(&path, &inline)
parse_source(&source).map_err(|err| Error::new(call_site, format!("{err:?}")))?; .map_err(|err| Error::new(call_site, format!("{err:?}")))?;
let world = resolve let world = resolve
.select_world(pkg, world.as_deref()) .select_world(pkg, world.as_deref())
.map_err(|e| Error::new(call_site, format!("{e:?}")))?; .map_err(|e| Error::new(call_site, format!("{e:?}")))?;
@@ -111,11 +114,15 @@ impl Parse for Config {
} }
} }
fn parse_source(source: &Option<Source>) -> anyhow::Result<(Resolve, PackageId, Vec<PathBuf>)> { fn parse_source(
path: &Option<String>,
inline: &Option<String>,
) -> anyhow::Result<(Resolve, PackageId, Vec<PathBuf>)> {
let mut resolve = Resolve::default(); let mut resolve = Resolve::default();
let mut files = Vec::new(); let mut files = Vec::new();
let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let mut parse = |path: &Path| -> anyhow::Result<_> {
let mut parse = |resolve: &mut Resolve, path: &Path| -> anyhow::Result<_> {
if path.is_dir() { if path.is_dir() {
let (pkg, sources) = resolve.push_dir(path)?; let (pkg, sources) = resolve.push_dir(path)?;
files = sources; files = sources;
@@ -126,15 +133,32 @@ fn parse_source(source: &Option<Source>) -> anyhow::Result<(Resolve, PackageId,
resolve.push(pkg, &Default::default()) resolve.push(pkg, &Default::default())
} }
}; };
let pkg = match source {
Some(Source::Inline(s)) => resolve.push( let path_pkg = if let Some(path) = path {
UnresolvedPackage::parse("macro-input".as_ref(), s)?, Some(parse(&mut resolve, &root.join(&path))?)
&Default::default(), } else {
)?, None
Some(Source::Path(s)) => parse(&root.join(s))?,
None => parse(&root.join("wit"))?,
}; };
let inline_pkg = if let Some(inline) = inline {
let deps = resolve
.packages
.iter()
.map(|(id, p)| (p.name.clone(), id))
.collect();
Some(resolve.push(
UnresolvedPackage::parse("macro-input".as_ref(), &inline)?,
&deps,
)?)
} else {
None
};
let pkg = inline_pkg
.or(path_pkg)
.map_or_else(|| parse(&mut resolve, &root.join("wit")), Ok)?;
Ok((resolve, pkg, files)) Ok((resolve, pkg, files))
} }