Skip to content

Commit 502ef12

Browse files
authored
Support to parse WIT files from multiple paths (#1003)
1 parent e103334 commit 502ef12

File tree

5 files changed

+78
-33
lines changed

5 files changed

+78
-33
lines changed

crates/guest-rust/macro/src/lib.rs

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::path::{Path, PathBuf};
55
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
66
use syn::parse::{Error, Parse, ParseStream, Result};
77
use syn::punctuated::Punctuated;
8-
use syn::{braced, token, Token};
8+
use syn::spanned::Spanned;
9+
use syn::{braced, token, LitStr, Token};
910
use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId};
1011
use wit_bindgen_rust::{Opts, Ownership, WithOption};
1112

@@ -50,9 +51,9 @@ struct Config {
5051
/// The source of the wit package definition
5152
enum Source {
5253
/// A path to a wit directory
53-
Path(String),
54+
Paths(Vec<PathBuf>),
5455
/// Inline sources have an optional path to a directory of their dependencies
55-
Inline(String, Option<PathBuf>),
56+
Inline(String, Option<Vec<PathBuf>>),
5657
}
5758

5859
impl Parse for Config {
@@ -69,15 +70,15 @@ impl Parse for Config {
6970
let fields = Punctuated::<Opt, Token![,]>::parse_terminated(&content)?;
7071
for field in fields.into_pairs() {
7172
match field.into_value() {
72-
Opt::Path(s) => {
73+
Opt::Path(span, p) => {
74+
let paths = p.into_iter().map(|f| PathBuf::from(f.value())).collect();
75+
7376
source = Some(match source {
74-
Some(Source::Path(_)) | Some(Source::Inline(_, Some(_))) => {
75-
return Err(Error::new(s.span(), "cannot specify second source"));
77+
Some(Source::Paths(_)) | Some(Source::Inline(_, Some(_))) => {
78+
return Err(Error::new(span, "cannot specify second source"));
7679
}
77-
Some(Source::Inline(i, None)) => {
78-
Source::Inline(i, Some(PathBuf::from(s.value())))
79-
}
80-
None => Source::Path(s.value()),
80+
Some(Source::Inline(i, None)) => Source::Inline(i, Some(paths)),
81+
None => Source::Paths(paths),
8182
})
8283
}
8384
Opt::World(s) => {
@@ -91,9 +92,7 @@ impl Parse for Config {
9192
Some(Source::Inline(_, _)) => {
9293
return Err(Error::new(s.span(), "cannot specify second source"));
9394
}
94-
Some(Source::Path(p)) => {
95-
Source::Inline(s.value(), Some(PathBuf::from(p)))
96-
}
95+
Some(Source::Paths(p)) => Source::Inline(s.value(), Some(p)),
9796
None => Source::Inline(s.value(), None),
9897
})
9998
}
@@ -143,7 +142,9 @@ impl Parse for Config {
143142
} else {
144143
world = input.parse::<Option<syn::LitStr>>()?.map(|s| s.value());
145144
if input.parse::<Option<syn::token::In>>()?.is_some() {
146-
source = Some(Source::Path(input.parse::<syn::LitStr>()?.value()));
145+
source = Some(Source::Paths(vec![PathBuf::from(
146+
input.parse::<syn::LitStr>()?.value(),
147+
)]));
147148
}
148149
}
149150
let (resolve, pkgs, files) =
@@ -168,31 +169,36 @@ fn parse_source(
168169
let mut resolve = Resolve::default();
169170
resolve.features.extend(features.iter().cloned());
170171
let mut files = Vec::new();
172+
let mut pkgs = Vec::new();
171173
let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
172-
let mut parse = |path: &Path| -> anyhow::Result<_> {
173-
// Try to normalize the path to make the error message more understandable when
174-
// the path is not correct. Fallback to the original path if normalization fails
175-
// (probably return an error somewhere else).
176-
let normalized_path = match std::fs::canonicalize(path) {
177-
Ok(p) => p,
178-
Err(_) => path.to_path_buf(),
179-
};
180-
let (pkg, sources) = resolve.push_path(normalized_path)?;
181-
files.extend(sources);
182-
Ok(pkg)
174+
let mut parse = |paths: &[PathBuf]| -> anyhow::Result<()> {
175+
for path in paths {
176+
let p = root.join(path);
177+
// Try to normalize the path to make the error message more understandable when
178+
// the path is not correct. Fallback to the original path if normalization fails
179+
// (probably return an error somewhere else).
180+
let normalized_path = match std::fs::canonicalize(&p) {
181+
Ok(p) => p,
182+
Err(_) => p.to_path_buf(),
183+
};
184+
let (pkg, sources) = resolve.push_path(normalized_path)?;
185+
pkgs.extend(pkg);
186+
files.extend(sources);
187+
}
188+
Ok(())
183189
};
184-
let pkg = match source {
190+
match source {
185191
Some(Source::Inline(s, path)) => {
186192
if let Some(p) = path {
187-
parse(&root.join(p))?;
193+
parse(p)?;
188194
}
189-
resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?
195+
pkgs = resolve.push_group(UnresolvedPackageGroup::parse("macro-input", s)?)?;
190196
}
191-
Some(Source::Path(s)) => parse(&root.join(s))?,
192-
None => parse(&root.join("wit"))?,
197+
Some(Source::Paths(p)) => parse(p)?,
198+
None => parse(&vec![root.join("wit")])?,
193199
};
194200

195-
Ok((resolve, pkg, files))
201+
Ok((resolve, pkgs, files))
196202
}
197203

198204
impl Config {
@@ -298,7 +304,7 @@ impl From<ExportKey> for wit_bindgen_rust::ExportKey {
298304

299305
enum Opt {
300306
World(syn::LitStr),
301-
Path(syn::LitStr),
307+
Path(Span, Vec<syn::LitStr>),
302308
Inline(syn::LitStr),
303309
UseStdFeature,
304310
RawStrings,
@@ -327,7 +333,18 @@ impl Parse for Opt {
327333
if l.peek(kw::path) {
328334
input.parse::<kw::path>()?;
329335
input.parse::<Token![:]>()?;
330-
Ok(Opt::Path(input.parse()?))
336+
// the `path` supports two forms:
337+
// * path: "xxx"
338+
// * path: ["aaa", "bbb"]
339+
if input.peek(token::Bracket) {
340+
let contents;
341+
syn::bracketed!(contents in input);
342+
let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
343+
Ok(Opt::Path(list.span(), list.into_iter().collect()))
344+
} else {
345+
let path: LitStr = input.parse()?;
346+
Ok(Opt::Path(path.span(), vec![path]))
347+
}
331348
} else if l.peek(kw::inline) {
332349
input.parse::<kw::inline>()?;
333350
input.parse::<Token![:]>()?;

crates/guest-rust/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,12 @@
647647
///
648648
/// // Path to parse WIT and its dependencies from. Defaults to the `wit`
649649
/// // folder adjacent to your `Cargo.toml`.
650+
/// //
651+
/// // This parameter also supports the form of a list, such as:
652+
/// // ["../path/to/wit1", "../path/to/wit2"]
653+
/// // Usually used in testing, our test suite may want to generate code
654+
/// // from wit files located in multiple paths within a single mod, and we
655+
/// // don't want to copy these files again.
650656
/// path: "../path/to/wit",
651657
///
652658
/// // Enables passing "inline WIT". If specified this is the default

crates/rust/tests/codegen.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,19 @@ mod simple_with_option {
556556
});
557557
}
558558
}
559+
560+
#[allow(unused)]
561+
mod multiple_paths {
562+
wit_bindgen::generate!({
563+
inline: r#"
564+
package test:paths;
565+
566+
world test {
567+
import paths:path1/test;
568+
export paths:path2/test;
569+
}
570+
"#,
571+
path: ["tests/wit/path1", "tests/wit/path2"],
572+
generate_all,
573+
});
574+
}

crates/rust/tests/wit/path1/world.wit

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package paths:path1;
2+
3+
interface test {}

crates/rust/tests/wit/path2/world.wit

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package paths:path2;
2+
3+
interface test {}

0 commit comments

Comments
 (0)