Skip to content

Commit d73c073

Browse files
authored
feat: Allow variants and records to be ignored by additional_derives (#1199)
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.
1 parent 88374b4 commit d73c073

File tree

4 files changed

+74
-8
lines changed

4 files changed

+74
-8
lines changed

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ impl Parse for Config {
115115
.map(|p| p.into_token_stream().to_string())
116116
.collect()
117117
}
118+
Opt::AdditionalDerivesIgnore(list) => {
119+
opts.additional_derive_ignore =
120+
list.into_iter().map(|i| i.value()).collect()
121+
}
118122
Opt::With(with) => opts.with.extend(with),
119123
Opt::GenerateAll => {
120124
opts.generate_all = true;
@@ -323,6 +327,7 @@ mod kw {
323327
syn::custom_keyword!(stubs);
324328
syn::custom_keyword!(export_prefix);
325329
syn::custom_keyword!(additional_derives);
330+
syn::custom_keyword!(additional_derives_ignore);
326331
syn::custom_keyword!(with);
327332
syn::custom_keyword!(generate_all);
328333
syn::custom_keyword!(type_section_suffix);
@@ -383,6 +388,7 @@ enum Opt {
383388
ExportPrefix(syn::LitStr),
384389
// Parse as paths so we can take the concrete types/macro names rather than raw strings
385390
AdditionalDerives(Vec<syn::Path>),
391+
AdditionalDerivesIgnore(Vec<syn::LitStr>),
386392
With(HashMap<String, WithOption>),
387393
GenerateAll,
388394
TypeSectionSuffix(syn::LitStr),
@@ -496,6 +502,13 @@ impl Parse for Opt {
496502
syn::bracketed!(contents in input);
497503
let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
498504
Ok(Opt::AdditionalDerives(list.iter().cloned().collect()))
505+
} else if l.peek(kw::additional_derives_ignore) {
506+
input.parse::<kw::additional_derives_ignore>()?;
507+
input.parse::<Token![:]>()?;
508+
let contents;
509+
syn::bracketed!(contents in input);
510+
let list = Punctuated::<_, Token![,]>::parse_terminated(&contents)?;
511+
Ok(Opt::AdditionalDerivesIgnore(list.iter().cloned().collect()))
499512
} else if l.peek(kw::with) {
500513
input.parse::<kw::with>()?;
501514
input.parse::<Token![:]>()?;

crates/rust/src/interface.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,7 +1821,15 @@ pub mod vtable{ordinal} {{
18211821
.collect();
18221822
for (name, mode) in self.modes_of(id) {
18231823
self.rustdoc(docs);
1824-
let mut derives = additional_derives.clone();
1824+
let mut derives = BTreeSet::new();
1825+
if !self
1826+
.gen
1827+
.opts
1828+
.additional_derive_ignore
1829+
.contains(&name.to_kebab_case())
1830+
{
1831+
derives.extend(additional_derives.clone());
1832+
}
18251833
if info.is_copy() {
18261834
self.push_str("#[repr(C)]\n");
18271835
derives.extend(["Copy", "Clone"].into_iter().map(|s| s.to_string()));
@@ -1924,7 +1932,15 @@ pub mod vtable{ordinal} {{
19241932
.collect();
19251933
for (name, mode) in self.modes_of(id) {
19261934
self.rustdoc(docs);
1927-
let mut derives = additional_derives.clone();
1935+
let mut derives = BTreeSet::new();
1936+
if !self
1937+
.gen
1938+
.opts
1939+
.additional_derive_ignore
1940+
.contains(&name.to_kebab_case())
1941+
{
1942+
derives.extend(additional_derives.clone());
1943+
}
19281944
if info.is_copy() {
19291945
derives.extend(["Copy", "Clone"].into_iter().map(|s| s.to_string()));
19301946
} else if info.is_clone() {
@@ -2072,13 +2088,15 @@ pub mod vtable{ordinal} {{
20722088
self.int_repr(enum_.tag());
20732089
self.push_str(")]\n");
20742090
// We use a BTree set to make sure we don't have any duplicates and a stable order
2075-
let mut derives: BTreeSet<String> = self
2091+
let mut derives: BTreeSet<String> = BTreeSet::new();
2092+
if !self
20762093
.gen
20772094
.opts
2078-
.additional_derive_attributes
2079-
.iter()
2080-
.cloned()
2081-
.collect();
2095+
.additional_derive_ignore
2096+
.contains(&name.to_kebab_case())
2097+
{
2098+
derives.extend(self.gen.opts.additional_derive_attributes.to_vec());
2099+
}
20822100
derives.extend(
20832101
["Clone", "Copy", "PartialEq", "Eq", "PartialOrd", "Ord"]
20842102
.into_iter()

crates/rust/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,15 @@ pub struct Opts {
248248
#[cfg_attr(feature = "clap", arg(long = "additional_derive_attribute", short = 'd', default_values_t = Vec::<String>::new()))]
249249
pub additional_derive_attributes: Vec<String>,
250250

251+
/// Variants and records to ignore when applying additional derive attributes.
252+
///
253+
/// These names are specified as they are listed in the wit file, i.e. in kebab case.
254+
/// This feature allows some variants and records to use types for which adding traits will cause
255+
/// compilation to fail, such as serde::Deserialize on wasi:io/streams.
256+
///
257+
#[cfg_attr(feature = "clap", arg(long = "additional_derive_ignore", default_values_t = Vec::<String>::new()))]
258+
pub additional_derive_ignore: Vec<String>,
259+
251260
/// Remapping of wit interface and type names to Rust module names and types.
252261
///
253262
/// Argument must be of the form `k=v` and this option can be passed
@@ -1030,6 +1039,13 @@ impl WorldGenerator for RustWasm {
10301039
self.opts.additional_derive_attributes
10311040
);
10321041
}
1042+
if !self.opts.additional_derive_ignore.is_empty() {
1043+
uwriteln!(
1044+
self.src_preamble,
1045+
"// * additional derives ignored {:?}",
1046+
self.opts.additional_derive_ignore
1047+
);
1048+
}
10331049
for (k, v) in self.opts.with.iter() {
10341050
uwriteln!(self.src_preamble, "// * with {k:?} = {v}");
10351051
}

crates/rust/tests/codegen.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,26 @@ mod custom_derives {
388388
inline: "
389389
package my:inline;
390390
391+
interface blag {
392+
resource input-stream {
393+
read: func(len: u64) -> list<u8>;
394+
}
395+
}
396+
391397
interface blah {
398+
use blag.{input-stream};
392399
record foo {
393400
field1: string,
394401
field2: list<u32>
395402
}
396403
397404
bar: func(cool: foo);
405+
406+
variant ignoreme {
407+
stream-type(input-stream),
408+
}
409+
410+
barry: func(warm: ignoreme);
398411
}
399412
400413
world baz {
@@ -405,9 +418,10 @@ mod custom_derives {
405418
// Clone is included by default almost everywhere, so include it here to make sure it
406419
// doesn't conflict
407420
additional_derives: [serde::Serialize, serde::Deserialize, Hash, Clone, PartialEq, Eq],
421+
additional_derives_ignore: ["ignoreme"],
408422
});
409423

410-
use exports::my::inline::blah::Foo;
424+
use exports::my::inline::blah::{Foo, Ignoreme};
411425

412426
struct Component;
413427

@@ -423,6 +437,11 @@ mod custom_derives {
423437
// compilation will fail here
424438
let _ = serde_json::to_string(&cool);
425439
}
440+
441+
fn barry(warm: Ignoreme) {
442+
// Compilation would fail if serde::Deserialize was applied to Ignoreme
443+
let _ = warm;
444+
}
426445
}
427446

428447
export!(Component);

0 commit comments

Comments
 (0)