Skip to content
Merged
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
105 changes: 66 additions & 39 deletions crates/guest-rust/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,31 @@ pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
.into()
}

fn anyhow_to_syn(span: Span, err: anyhow::Error) -> Error {
fn render_error(
err: &(dyn std::error::Error + 'static),
sourcemap: &Option<&wit_bindgen_core::wit_parser::SourceMap>,
) -> String {
if let Some(sourcemap) = sourcemap {
if let Some(e) = err.downcast_ref::<wit_bindgen_core::wit_parser::ParseError>() {
return e.render(sourcemap);
}
if let Some(e) = err.downcast_ref::<wit_bindgen_core::wit_parser::ResolveError>() {
return e.render(sourcemap);
}
}

err.to_string()
}

fn anyhow_to_syn(
span: Span,
err: anyhow::Error,
sourcemap: Option<&wit_bindgen_core::wit_parser::SourceMap>,
) -> Error {
let err = attach_with_context(err);
let mut msg = err.to_string();
let mut msg = render_error(err.as_ref(), &sourcemap);
for cause in err.chain().skip(1) {
let cause = render_error(cause, &sourcemap);
msg.push_str(&format!("\n\nCaused by:\n {cause}"));
}
Error::new(span, msg)
Expand Down Expand Up @@ -193,11 +214,10 @@ impl Parse for Config {
)]));
}
}
let (resolve, main_packages, files) =
parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?;
let (resolve, main_packages, files) = parse_source(&source, &features)?;
let world = resolve
.select_world(&main_packages, world.as_deref())
.map_err(|e| anyhow_to_syn(call_site, e))?;
.map_err(|e| anyhow_to_syn(call_site, e, None))?;
Ok(Config {
opts,
resolve,
Expand All @@ -212,50 +232,57 @@ impl Parse for Config {
fn parse_source(
source: &Option<Source>,
features: &[String],
) -> anyhow::Result<(Resolve, Vec<PackageId>, Vec<PathBuf>)> {
) -> syn::Result<(Resolve, Vec<PackageId>, Vec<PathBuf>)> {
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 = |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.push(pkg);
files.extend(sources.paths().map(|p| p.to_owned()));
}
Ok(())
};
let default = root.join("wit");
match source {
Some(Source::Inline(s, path)) => {
match path {
Some(p) => parse(p)?,
// If no `path` is explicitly specified still parse the default
// `wit` directory if it exists. Don't require its existence,
// however, as `inline` can be used in lieu of a folder. Test
// whether it exists and only if there is it parsed.
None => {
if default.exists() {
parse(&[default])?;

let mut try_parse = || -> anyhow::Result<()> {
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.push(pkg);
files.extend(sources.paths().map(|p| p.to_owned()));
}
Ok(())
};
let default = root.join("wit");

match source {
Some(Source::Inline(s, path)) => {
match path {
Some(p) => parse(p)?,
// If no `path` is explicitly specified still parse the default
// `wit` directory if it exists. Don't require its existence,
// however, as `inline` can be used in lieu of a folder. Test
// whether it exists and only if there is it parsed.
None => {
if default.exists() {
parse(&[default])?;
}
}
}
pkgs.clear();
pkgs.push(resolve.push_str("macro-input", s)?);
Ok(())
}
pkgs.clear();
pkgs.push(resolve.push_str("macro-input", s)?);
Some(Source::Paths(p)) => parse(p),
None => parse(&[default]),
}
Some(Source::Paths(p)) => parse(p)?,
None => parse(&[default])?,
};

try_parse().map_err(|e| anyhow_to_syn(Span::call_site(), e, Some(&resolve.source_map)))?;

Ok((resolve, pkgs, files))
}

Expand All @@ -265,7 +292,7 @@ impl Config {
let mut generator = self.opts.build();
generator
.generate(&mut self.resolve, self.world, &mut files)
.map_err(|e| anyhow_to_syn(Span::call_site(), e))?;
.map_err(|e| anyhow_to_syn(Span::call_site(), e, None))?;
let (_, src) = files.iter().next().unwrap();
let mut src = std::str::from_utf8(src).unwrap().to_string();

Expand Down
Loading