From 09ad09194db72d9bd9296168b88f5d18cf34338e Mon Sep 17 00:00:00 2001 From: Colin Murphy Date: Fri, 7 Mar 2025 18:21:33 -0500 Subject: [PATCH] feat: Allow variants and records to be ignored by additional_derives This feature allows some variants and records to use types for which adding traits will cause compilation to fail, such as serde::Deserialize on wasi:io/streams. Variants and records are specified as they are listed in the wit file, i.e. in kebab case. --- crates/guest-rust/macro/src/lib.rs | 13 ++++++++++++ crates/rust/src/interface.rs | 32 +++++++++++++++++++++++------- crates/rust/src/lib.rs | 16 +++++++++++++++ crates/rust/tests/codegen.rs | 21 +++++++++++++++++++- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 24a04f291..b4be3798c 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -115,6 +115,10 @@ impl Parse for Config { .map(|p| p.into_token_stream().to_string()) .collect() } + Opt::AdditionalDerivesIgnore(list) => { + opts.additional_derive_ignore = + list.into_iter().map(|i| i.value()).collect() + } Opt::With(with) => opts.with.extend(with), Opt::GenerateAll => { opts.generate_all = true; @@ -323,6 +327,7 @@ mod kw { syn::custom_keyword!(stubs); syn::custom_keyword!(export_prefix); syn::custom_keyword!(additional_derives); + syn::custom_keyword!(additional_derives_ignore); syn::custom_keyword!(with); syn::custom_keyword!(generate_all); syn::custom_keyword!(type_section_suffix); @@ -383,6 +388,7 @@ enum Opt { ExportPrefix(syn::LitStr), // Parse as paths so we can take the concrete types/macro names rather than raw strings AdditionalDerives(Vec), + AdditionalDerivesIgnore(Vec), With(HashMap), GenerateAll, TypeSectionSuffix(syn::LitStr), @@ -496,6 +502,13 @@ impl Parse for Opt { syn::bracketed!(contents in input); let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?; Ok(Opt::AdditionalDerives(list.iter().cloned().collect())) + } else if l.peek(kw::additional_derives_ignore) { + input.parse::()?; + input.parse::()?; + let contents; + syn::bracketed!(contents in input); + let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?; + Ok(Opt::AdditionalDerivesIgnore(list.iter().cloned().collect())) } else if l.peek(kw::with) { input.parse::()?; input.parse::()?; diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 87ff5533a..f99fe1eeb 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1821,7 +1821,15 @@ pub mod vtable{ordinal} {{ .collect(); for (name, mode) in self.modes_of(id) { self.rustdoc(docs); - let mut derives = additional_derives.clone(); + let mut derives = BTreeSet::new(); + if !self + .gen + .opts + .additional_derive_ignore + .contains(&name.to_kebab_case()) + { + derives.extend(additional_derives.clone()); + } if info.is_copy() { self.push_str("#[repr(C)]\n"); derives.extend(["Copy", "Clone"].into_iter().map(|s| s.to_string())); @@ -1924,7 +1932,15 @@ pub mod vtable{ordinal} {{ .collect(); for (name, mode) in self.modes_of(id) { self.rustdoc(docs); - let mut derives = additional_derives.clone(); + let mut derives = BTreeSet::new(); + if !self + .gen + .opts + .additional_derive_ignore + .contains(&name.to_kebab_case()) + { + derives.extend(additional_derives.clone()); + } if info.is_copy() { derives.extend(["Copy", "Clone"].into_iter().map(|s| s.to_string())); } else if info.is_clone() { @@ -2072,13 +2088,15 @@ pub mod vtable{ordinal} {{ self.int_repr(enum_.tag()); self.push_str(")]\n"); // We use a BTree set to make sure we don't have any duplicates and a stable order - let mut derives: BTreeSet = self + let mut derives: BTreeSet = BTreeSet::new(); + if !self .gen .opts - .additional_derive_attributes - .iter() - .cloned() - .collect(); + .additional_derive_ignore + .contains(&name.to_kebab_case()) + { + derives.extend(self.gen.opts.additional_derive_attributes.to_vec()); + } derives.extend( ["Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord"] .into_iter() diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 0807f11e5..5fabffdc7 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -248,6 +248,15 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long = "additional_derive_attribute", short = 'd', default_values_t = Vec::::new()))] pub additional_derive_attributes: Vec, + /// Variants and records to ignore when applying additional derive attributes. + /// + /// These names are specified as they are listed in the wit file, i.e. in kebab case. + /// This feature allows some variants and records to use types for which adding traits will cause + /// compilation to fail, such as serde::Deserialize on wasi:io/streams. + /// + #[cfg_attr(feature = "clap", arg(long = "additional_derive_ignore", default_values_t = Vec::::new()))] + pub additional_derive_ignore: Vec, + /// Remapping of wit interface and type names to Rust module names and types. /// /// Argument must be of the form `k=v` and this option can be passed @@ -1030,6 +1039,13 @@ impl WorldGenerator for RustWasm { self.opts.additional_derive_attributes ); } + if !self.opts.additional_derive_ignore.is_empty() { + uwriteln!( + self.src_preamble, + "// * additional derives ignored {:?}", + self.opts.additional_derive_ignore + ); + } for (k, v) in self.opts.with.iter() { uwriteln!(self.src_preamble, "// * with {k:?} = {v}"); } diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index 6568c65b6..46b2379df 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -388,13 +388,26 @@ mod custom_derives { inline: " package my:inline; + interface blag { + resource input-stream { + read: func(len: u64) -> list; + } + } + interface blah { + use blag.{input-stream}; record foo { field1: string, field2: list } bar: func(cool: foo); + + variant ignoreme { + stream-type(input-stream), + } + + barry: func(warm: ignoreme); } world baz { @@ -405,9 +418,10 @@ mod custom_derives { // Clone is included by default almost everywhere, so include it here to make sure it // doesn't conflict additional_derives: [serde::Serialize, serde::Deserialize, Hash, Clone, PartialEq, Eq], + additional_derives_ignore: ["ignoreme"], }); - use exports::my::inline::blah::Foo; + use exports::my::inline::blah::{Foo, Ignoreme}; struct Component; @@ -423,6 +437,11 @@ mod custom_derives { // compilation will fail here let _ = serde_json::to_string(&cool); } + + fn barry(warm: Ignoreme) { + // Compilation would fail if serde::Deserialize was applied to Ignoreme + let _ = warm; + } } export!(Component);