Skip to content

Commit 814efc9

Browse files
authored
Add #[wasm_bindgen(getter_with_clone)] attribute (#2633)
* add getter_with_clone attribute * add docs for getter_with_clone * add tests for getter_with_clone
1 parent 0e69e0e commit 814efc9

File tree

7 files changed

+83
-4
lines changed

7 files changed

+83
-4
lines changed

crates/backend/src/ast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ pub struct StructField {
343343
pub comments: Vec<String>,
344344
/// Whether to generate a typescript definition for this field
345345
pub generate_typescript: bool,
346+
/// Whether to use .clone() in the auto-generated getter for this field
347+
pub getter_with_clone: bool,
346348
}
347349

348350
/// Information about an Enum being exported

crates/backend/src/codegen.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,8 +274,19 @@ impl ToTokens for ast::StructField {
274274
let getter = &self.getter;
275275
let setter = &self.setter;
276276

277-
let assert_copy = quote! { assert_copy::<#ty>() };
278-
let assert_copy = respan(assert_copy, ty);
277+
let maybe_assert_copy = if self.getter_with_clone {
278+
quote! {}
279+
} else {
280+
quote! { assert_copy::<#ty>() }
281+
};
282+
let maybe_assert_copy = respan(maybe_assert_copy, ty);
283+
284+
let maybe_clone = if self.getter_with_clone {
285+
quote! { .clone() }
286+
} else {
287+
quote! {}
288+
};
289+
279290
(quote! {
280291
#[doc(hidden)]
281292
#[allow(clippy::all)]
@@ -287,11 +298,11 @@ impl ToTokens for ast::StructField {
287298
use wasm_bindgen::convert::IntoWasmAbi;
288299

289300
fn assert_copy<T: Copy>(){}
290-
#assert_copy;
301+
#maybe_assert_copy;
291302

292303
let js = js as *mut WasmRefCell<#struct_name>;
293304
assert_not_null(js);
294-
let val = (*js).borrow().#rust_name;
305+
let val = (*js).borrow().#rust_name#maybe_clone;
295306
<#ty as IntoWasmAbi>::into_abi(val)
296307
}
297308
})

crates/macro-support/src/parser.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ macro_rules! attrgen {
5959
(start, Start(Span)),
6060
(skip, Skip(Span)),
6161
(typescript_type, TypeScriptType(Span, String, Span)),
62+
(getter_with_clone, GetterWithClone(Span)),
6263

6364
// For testing purposes only.
6465
(assert_no_shim, AssertNoShim(Span)),
@@ -374,6 +375,7 @@ impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
374375
.map(|s| s.0.to_string())
375376
.unwrap_or(self.ident.to_string());
376377
let is_inspectable = attrs.inspectable().is_some();
378+
let getter_with_clone = attrs.getter_with_clone().is_some();
377379
for (i, field) in self.fields.iter_mut().enumerate() {
378380
match field.vis {
379381
syn::Visibility::Public(..) => {}
@@ -410,6 +412,7 @@ impl<'a> ConvertToAst<BindgenAttrs> for &'a mut syn::ItemStruct {
410412
setter: Ident::new(&setter, Span::call_site()),
411413
comments,
412414
generate_typescript: attrs.skip_typescript().is_none(),
415+
getter_with_clone: getter_with_clone || attrs.getter_with_clone().is_some(),
413416
});
414417
attrs.check_used()?;
415418
}

guide/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
- [`inspectable`](./reference/attributes/on-rust-exports/inspectable.md)
8888
- [`skip_typescript`](./reference/attributes/on-rust-exports/skip_typescript.md)
8989
- [`typescript_type`](./reference/attributes/on-rust-exports/typescript_type.md)
90+
- [`getter_with_clone`](./reference/attributes/on-rust-exports/getter_with_clone.md)
9091

9192
- [`web-sys`](./web-sys/index.md)
9293
- [Using `web-sys`](./web-sys/using-web-sys.md)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# `getter_with_clone`
2+
3+
By default, Rust exports exposed to JavaScript will generate getters that require fields to implement `Copy`. The `getter_with_clone` attribute can be used to generate getters that require `Clone` instead. This attribute can be applied per struct or per field. For example:
4+
5+
```rust
6+
#[wasm_bindgen]
7+
pub struct Foo {
8+
#[wasm_bindgen(getter_with_clone)]
9+
pub bar: String,
10+
}
11+
12+
#[wasm_bindgen(getter_with_clone)]
13+
pub struct Foo {
14+
pub bar: String,
15+
pub baz: String,
16+
}
17+
```

tests/wasm/classes.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ exports.js_public_fields = () => {
112112
assert.strictEqual(a.skipped, undefined);
113113
};
114114

115+
exports.js_getter_with_clone = () => {
116+
const a = wasm.GetterWithCloneStruct.new();
117+
assert.strictEqual(a.a, '');
118+
a.a = 'foo';
119+
assert.strictEqual(a.a, 'foo');
120+
121+
const b = wasm.GetterWithCloneStructField.new();
122+
assert.strictEqual(b.a, '');
123+
b.a = 'foo';
124+
assert.strictEqual(b.a, 'foo');
125+
};
126+
115127
exports.js_using_self = () => {
116128
wasm.UseSelf.new().free();
117129
};

tests/wasm/classes.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern "C" {
1616
fn js_constructors();
1717
fn js_empty_structs();
1818
fn js_public_fields();
19+
fn js_getter_with_clone();
1920
fn js_using_self();
2021
fn js_readonly_fields();
2122
fn js_double_consume();
@@ -288,6 +289,38 @@ impl PublicFields {
288289
}
289290
}
290291

292+
#[wasm_bindgen_test]
293+
fn getter_with_clone() {
294+
js_getter_with_clone();
295+
}
296+
297+
#[wasm_bindgen(getter_with_clone)]
298+
#[derive(Default)]
299+
pub struct GetterWithCloneStruct {
300+
pub a: String,
301+
}
302+
303+
#[wasm_bindgen]
304+
impl GetterWithCloneStruct {
305+
pub fn new() -> GetterWithCloneStruct {
306+
GetterWithCloneStruct::default()
307+
}
308+
}
309+
310+
#[wasm_bindgen]
311+
#[derive(Default)]
312+
pub struct GetterWithCloneStructField {
313+
#[wasm_bindgen(getter_with_clone)]
314+
pub a: String,
315+
}
316+
317+
#[wasm_bindgen]
318+
impl GetterWithCloneStructField {
319+
pub fn new() -> GetterWithCloneStructField {
320+
GetterWithCloneStructField::default()
321+
}
322+
}
323+
291324
#[wasm_bindgen_test]
292325
fn using_self() {
293326
js_using_self();

0 commit comments

Comments
 (0)