Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to parse WIT files from multiple paths #1003

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 50 additions & 33 deletions crates/guest-rust/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{braced, token, Token};
use syn::spanned::Spanned;
use syn::{braced, token, LitStr, Token};
use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId};
use wit_bindgen_rust::{Opts, Ownership, WithOption};

Expand Down Expand Up @@ -50,9 +51,9 @@ struct Config {
/// The source of the wit package definition
enum Source {
/// A path to a wit directory
Path(String),
Paths(Vec<PathBuf>),
/// Inline sources have an optional path to a directory of their dependencies
Inline(String, Option<PathBuf>),
Inline(String, Option<Vec<PathBuf>>),
}

impl Parse for Config {
Expand All @@ -69,15 +70,15 @@ impl Parse for Config {
let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
for field in fields.into_pairs() {
match field.into_value() {
Opt::Path(s) => {
Opt::Path(span, p) => {
let paths = p.into_iter().map(|f| PathBuf::from(f.value())).collect();

source = Some(match source {
Some(Source::Path(_)) | Some(Source::Inline(_, Some(_))) => {
return Err(Error::new(s.span(), "cannot specify second source"));
Some(Source::Paths(_)) | Some(Source::Inline(_, Some(_))) => {
return Err(Error::new(span, "cannot specify second source"));
}
Some(Source::Inline(i, None)) => {
Source::Inline(i, Some(PathBuf::from(s.value())))
}
None => Source::Path(s.value()),
Some(Source::Inline(i, None)) => Source::Inline(i, Some(paths)),
None => Source::Paths(paths),
})
}
Opt::World(s) => {
Expand All @@ -91,9 +92,7 @@ impl Parse for Config {
Some(Source::Inline(_, _)) => {
return Err(Error::new(s.span(), "cannot specify second source"));
}
Some(Source::Path(p)) => {
Source::Inline(s.value(), Some(PathBuf::from(p)))
}
Some(Source::Paths(p)) => Source::Inline(s.value(), Some(p)),
None => Source::Inline(s.value(), None),
})
}
Expand Down Expand Up @@ -143,7 +142,9 @@ impl Parse for Config {
} else {
world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
if input.parse::<Option<syn::token::In>>()?.is_some() {
source = Some(Source::Path(input.parse::<syn::LitStr>()?.value()));
source = Some(Source::Paths(vec![PathBuf::from(
input.parse::<syn::LitStr>()?.value(),
)]));
}
}
let (resolve, pkgs, files) =
Expand All @@ -168,31 +169,36 @@ fn parse_source(
let mut resolve = Resolve::default();
resolve.features.extend(features.iter().cloned());
let mut files = Vec::new();
let mut pkgs = Vec::new();
let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let mut parse = |path: &Path| -> anyhow::Result<_> {
// Try to normalize the path to make the error message more understandable when
// the path is not correct. Fallback to the original path if normalization fails
// (probably return an error somewhere else).
let normalized_path = match std::fs::canonicalize(path) {
Ok(p) => p,
Err(_) => path.to_path_buf(),
};
let (pkg, sources) = resolve.push_path(normalized_path)?;
files.extend(sources);
Ok(pkg)
let mut parse = |paths: &[PathBuf]| -> anyhow::Result<()> {
for path in paths {
let p = root.join(path);
// Try to normalize the path to make the error message more understandable when
// the path is not correct. Fallback to the original path if normalization fails
// (probably return an error somewhere else).
let normalized_path = match std::fs::canonicalize(&p) {
Ok(p) => p,
Err(_) => p.to_path_buf(),
};
let (pkg, sources) = resolve.push_path(normalized_path)?;
pkgs.extend(pkg);
files.extend(sources);
}
Ok(())
};
let pkg = match source {
match source {
Some(Source::Inline(s, path)) => {
if let Some(p) = path {
parse(&root.join(p))?;
parse(p)?;
}
resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?
pkgs = resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?;
}
Some(Source::Path(s)) => parse(&root.join(s))?,
None => parse(&root.join("wit"))?,
Some(Source::Paths(p)) => parse(p)?,
None => parse(&vec![root.join("wit")])?,
};

Ok((resolve, pkg, files))
Ok((resolve, pkgs, files))
}

impl Config {
Expand Down Expand Up @@ -298,7 +304,7 @@ impl From<ExportKey> for wit_bindgen_rust::ExportKey {

enum Opt {
World(syn::LitStr),
Path(syn::LitStr),
Path(Span, Vec<syn::LitStr>),
Inline(syn::LitStr),
UseStdFeature,
RawStrings,
Expand Down Expand Up @@ -327,7 +333,18 @@ impl Parse for Opt {
if l.peek(kw::path) {
input.parse::<kw::path>()?;
input.parse::<Token![:]>()?;
Ok(Opt::Path(input.parse()?))
// the `path` supports two forms:
// * path: "xxx"
// * path: ["aaa", "bbb"]
if input.peek(token::Bracket) {
let contents;
syn::bracketed!(contents in input);
let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
Ok(Opt::Path(list.span(), list.into_iter().collect()))
} else {
let path: LitStr = input.parse()?;
Ok(Opt::Path(path.span(), vec![path]))
}
} else if l.peek(kw::inline) {
input.parse::<kw::inline>()?;
input.parse::<Token![:]>()?;
Expand Down
6 changes: 6 additions & 0 deletions crates/guest-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,12 @@
///
/// // Path to parse WIT and its dependencies from. Defaults to the `wit`
/// // folder adjacent to your `Cargo.toml`.
/// //
/// // This parameter also supports the form of a list, such as:
/// // ["../path/to/wit1", "../path/to/wit2"]
/// // Usually used in testing, our test suite may want to generate code
/// // from wit files located in multiple paths within a single mod, and we
/// // don't want to copy these files again.
/// path: "../path/to/wit",
///
/// // Enables passing "inline WIT". If specified this is the default
Expand Down
16 changes: 16 additions & 0 deletions crates/rust/tests/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,3 +556,19 @@ mod simple_with_option {
});
}
}

#[allow(unused)]
mod multiple_paths {
wit_bindgen::generate!({
inline: r#"
package test:paths;
world test {
import paths:path1/test;
export paths:path2/test;
}
"#,
path: ["tests/wit/path1", "tests/wit/path2"],
generate_all,
});
}
3 changes: 3 additions & 0 deletions crates/rust/tests/wit/path1/world.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package paths:path1;

interface test {}
3 changes: 3 additions & 0 deletions crates/rust/tests/wit/path2/world.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package paths:path2;

interface test {}
Loading