Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
cpetig committed Jun 14, 2024
2 parents 72e0eb2 + 0fdc730 commit 41f5ea0
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 98 deletions.
21 changes: 17 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ heck = { version = "0.5" }
pulldown-cmark = { version = "0.9", default-features = false }
clap = { version = "4.3.19", features = ["derive"] }
indexmap = "2.0.0"
prettyplease = "0.2.20"
syn = { version = "2.0", features = ["printing"] }

wasmparser = "0.209.0"
wasm-encoder = "0.209.0"
Expand Down
4 changes: 3 additions & 1 deletion crates/c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl WorldGenerator for C {
name: &WorldKey,
id: InterfaceId,
_files: &mut Files,
) {
) -> Result<()> {
let wasm_import_module = resolve.name_world_key(name);
let mut gen = self.interface(resolve, true, Some(&wasm_import_module));
gen.interface = Some((id, name));
Expand All @@ -192,6 +192,8 @@ impl WorldGenerator for C {
}

gen.gen.src.append(&gen.src);

Ok(())
}

fn import_funcs(
Expand Down
6 changes: 4 additions & 2 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ pub trait WorldGenerator {
for (name, import) in world.imports.iter() {
match import {
WorldItem::Function(f) => funcs.push((unwrap_name(name), f)),
WorldItem::Interface { id, .. } => self.import_interface(resolve, name, *id, files),
WorldItem::Interface { id, .. } => {
self.import_interface(resolve, name, *id, files)?
}
WorldItem::Type(id) => types.push((unwrap_name(name), *id)),
}
}
Expand Down Expand Up @@ -91,7 +93,7 @@ pub trait WorldGenerator {
name: &WorldKey,
iface: InterfaceId,
files: &mut Files,
);
) -> Result<()>;

/// Called before any exported interfaces are generated.
fn pre_export_interface(&mut self, resolve: &Resolve, files: &mut Files) -> Result<()> {
Expand Down
4 changes: 4 additions & 0 deletions crates/core/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ impl Source {
pub fn as_mut_string(&mut self) -> &mut String {
&mut self.s
}

pub fn as_str(&self) -> &str {
&self.s
}
}

impl Write for Source {
Expand Down
4 changes: 3 additions & 1 deletion crates/csharp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl WorldGenerator for CSharp {
key: &WorldKey,
id: InterfaceId,
_files: &mut Files,
) {
) -> Result<()> {
let name = interface_name(self, resolve, key, Direction::Import);
self.interface_names.insert(id, name.clone());
let mut gen = self.interface(resolve, &name, Direction::Import);
Expand Down Expand Up @@ -232,6 +232,8 @@ impl WorldGenerator for CSharp {
gen.define_interface_types(id);

gen.add_interface_fragment(false);

Ok(())
}

fn import_funcs(
Expand Down
4 changes: 3 additions & 1 deletion crates/go/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl WorldGenerator for TinyGo {
name: &WorldKey,
id: InterfaceId,
_files: &mut Files,
) {
) -> Result<()> {
let name_raw = &resolve.name_world_key(name);
self.src
.push_str(&format!("// Import functions from {name_raw}\n"));
Expand All @@ -187,6 +187,8 @@ impl WorldGenerator for TinyGo {
let preamble = mem::take(&mut gen.preamble);
self.src.push_str(&src);
self.preamble.append_src(&preamble);

Ok(())
}

fn import_funcs(
Expand Down
4 changes: 3 additions & 1 deletion crates/guest-rust/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ test = false

[dependencies]
proc-macro2 = "1.0"
syn = { version = "2.0", features = ["printing"] }
quote = "1"
wit-bindgen-core = { workspace = true }
wit-bindgen-rust = { workspace = true }
anyhow = { workspace = true }
syn = { workspace = true }
prettyplease = { workspace = true }

39 changes: 27 additions & 12 deletions crates/guest-rust/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{braced, token, Token};
use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId};
use wit_bindgen_rust::{Opts, Ownership};
use wit_bindgen_rust::{Opts, Ownership, WithOption};

#[proc_macro]
pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Expand Down Expand Up @@ -102,6 +102,9 @@ impl Parse for Config {
.collect()
}
Opt::With(with) => opts.with.extend(with),
Opt::GenerateAll => {
opts.with.generate_by_default = true;
}
Opt::TypeSectionSuffix(suffix) => {
opts.type_section_suffix = Some(suffix.value());
}
Expand Down Expand Up @@ -194,15 +197,12 @@ impl Config {
let n = INVOCATION.fetch_add(1, Relaxed);
let path = root.join(format!("{world_name}{n}.rs"));

std::fs::write(&path, &src).unwrap();

// optimistically format the code but don't require success
drop(
std::process::Command::new("rustfmt")
.arg(&path)
.arg("--edition=2021")
.output(),
);
let contents = match fmt(&src) {
Ok(formatted) => formatted,
Err(_) => src.clone(),
};
std::fs::write(&path, contents.as_bytes()).unwrap();

src = format!("include!({path:?});");
}
Expand Down Expand Up @@ -240,6 +240,7 @@ mod kw {
syn::custom_keyword!(export_prefix);
syn::custom_keyword!(additional_derives);
syn::custom_keyword!(with);
syn::custom_keyword!(generate_all);
syn::custom_keyword!(type_section_suffix);
syn::custom_keyword!(disable_run_ctors_once_workaround);
syn::custom_keyword!(default_bindings_module);
Expand Down Expand Up @@ -291,7 +292,8 @@ enum Opt {
ExportPrefix(syn::LitStr),
// Parse as paths so we can take the concrete types/macro names rather than raw strings
AdditionalDerives(Vec<syn::Path>),
With(HashMap<String, String>),
With(HashMap<String, WithOption>),
GenerateAll,
TypeSectionSuffix(syn::LitStr),
DisableRunCtorsOnceWorkaround(syn::LitBool),
DefaultBindingsModule(syn::LitStr),
Expand Down Expand Up @@ -401,6 +403,9 @@ impl Parse for Opt {
let fields: Punctuated<_, Token![,]> =
contents.parse_terminated(with_field_parse, Token![,])?;
Ok(Opt::With(HashMap::from_iter(fields.into_iter())))
} else if l.peek(kw::generate_all) {
input.parse::<kw::generate_all>()?;
Ok(Opt::GenerateAll)
} else if l.peek(kw::type_section_suffix) {
input.parse::<kw::type_section_suffix>()?;
input.parse::<Token![:]>()?;
Expand Down Expand Up @@ -438,7 +443,7 @@ impl Parse for Opt {
}
}

fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> {
fn with_field_parse(input: ParseStream<'_>) -> Result<(String, WithOption)> {
let interface = input.parse::<syn::LitStr>()?.value();
input.parse::<Token![:]>()?;
let start = input.span();
Expand All @@ -449,6 +454,10 @@ fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> {
.join(path.segments.last().unwrap().ident.span())
.unwrap_or(start);

if path.is_ident("generate") {
return Ok((interface, WithOption::Generate));
}

let mut buf = String::new();
let append = |buf: &mut String, segment: syn::PathSegment| -> Result<()> {
if !segment.arguments.is_none() {
Expand Down Expand Up @@ -478,5 +487,11 @@ fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> {
append(&mut buf, segment)?;
}

Ok((interface, buf))
Ok((interface, WithOption::Path(buf)))
}

/// Format a valid Rust string
fn fmt(input: &str) -> Result<String> {
let syntax_tree = syn::parse_file(&input)?;
Ok(prettyplease::unparse(&syntax_tree))
}
5 changes: 5 additions & 0 deletions crates/guest-rust/src/examples/_1_interface_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ crate::generate!({
"#,

path: "[email protected]",

// specify that this interface dependency should be generated as well.
with: {
"wasi:cli/[email protected]": generate,
}
});
7 changes: 7 additions & 0 deletions crates/guest-rust/src/examples/_3_world_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ crate::generate!({

// provided to specify the path to `wasi:*` dependencies referenced above.
path: "[email protected]",

// specify that these interface dependencies should be generated as well.
with: {
"wasi:random/[email protected]": generate,
"wasi:clocks/[email protected]": generate,
"wasi:io/[email protected]": generate
}
});
23 changes: 14 additions & 9 deletions crates/guest-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,25 +668,30 @@
/// // By default this set is empty.
/// additional_derives: [PartialEq, Eq, Hash, Clone],
///
/// // When generating bindings for imports it might be the case that
/// // bindings were already generated in a different crate. For example
/// // if your world refers to WASI types then the `wasi` crate already
/// // has generated bindings for all WASI types and structures. In this
/// // When generating bindings for interfaces that are not defined in the
/// // same package as `world`, this option can be used to either generate
/// // those bindings or point to already generated bindings.
/// // For example, if your world refers to WASI types then the `wasi` crate
/// // already has generated bindings for all WASI types and structures. In this
/// // situation the key `with` here can be used to use those types
/// // elsewhere rather than regenerating types.
/// //
/// // The `with` key here only works for interfaces referred to by imported
/// // functions. Additionally it only supports replacing types at the
/// // interface level at this time.
/// // If, however, your world refers to interfaces for which you don't have
/// // already generated bindings then you can use the special `generate` value
/// // to have those bindings generated.
/// //
/// // When an interface is specified here no bindings will be generated at
/// // all. It's assumed bindings are fully generated upstream. This is an
/// // The `with` key only supports replacing types at the interface level
/// // at this time.
/// //
/// // When an interface is specified no bindings will be generated at
/// // all. It's assumed bindings are fully generated somewhere else. This is an
/// // indicator that any further references to types defined in these
/// // interfaces should use the upstream paths specified here instead.
/// //
/// // Any unused keys in this map are considered an error.
/// with: {
/// "wasi:io/poll": wasi::io::poll,
/// "some:package/my-interface": generate,
/// },
///
/// // An optional list of function names to skip generating bindings for.
Expand Down
4 changes: 3 additions & 1 deletion crates/markdown/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl WorldGenerator for Markdown {
name: &WorldKey,
id: InterfaceId,
_files: &mut Files,
) {
) -> Result<()> {
let name = resolve.name_world_key(name);
uwriteln!(
self.src,
Expand All @@ -131,6 +131,8 @@ impl WorldGenerator for Markdown {
gen.push_str("\n");
gen.types(id);
gen.funcs(id);

Ok(())
}

fn import_funcs(
Expand Down
2 changes: 2 additions & 0 deletions crates/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ wasm-metadata = { workspace = true }
heck = { workspace = true }
clap = { workspace = true, optional = true }
indexmap = { workspace = true }
syn = { workspace = true }
prettyplease = { workspace = true }

[dev-dependencies]
wit-bindgen = { path = '../guest-rust' }
Expand Down
Loading

0 comments on commit 41f5ea0

Please sign in to comment.