Skip to content

Commit dbda3f4

Browse files
Add rename_all = "camelCase" option to automatically rename methods and function
1 parent 76776ef commit dbda3f4

File tree

7 files changed

+346
-42
lines changed

7 files changed

+346
-42
lines changed

crates/backend/src/ast.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,6 @@ pub struct Function {
361361
pub name: String,
362362
/// The span of the function's name in Rust code
363363
pub name_span: Span,
364-
/// Whether the function has a js_name attribute
365-
pub renamed_via_js_name: bool,
366364
/// The arguments to the function
367365
pub arguments: Vec<syn::PatType>,
368366
/// The return type of the function, if provided
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* tslint:disable */
2+
/* eslint-disable */
3+
export function export_from_rust(a: number): number;
4+
export class RustStruct {
5+
free(): void;
6+
static i_dont_get_renamed(): void;
7+
incrementFoo(amount?: number): void;
8+
setFoo(foo: number): void;
9+
get_another(): number;
10+
another_field_for_you: number;
11+
foo: number;
12+
someCoolField: number;
13+
}

crates/cli/tests/reference/rename.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
let wasm;
2+
export function __wbg_set_wasm(val) {
3+
wasm = val;
4+
}
5+
6+
7+
function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }
8+
9+
const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;
10+
11+
let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
12+
13+
cachedTextDecoder.decode();
14+
15+
let cachedUint8ArrayMemory0 = null;
16+
17+
function getUint8ArrayMemory0() {
18+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
19+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
20+
}
21+
return cachedUint8ArrayMemory0;
22+
}
23+
24+
function getStringFromWasm0(ptr, len) {
25+
ptr = ptr >>> 0;
26+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
27+
}
28+
/**
29+
* @param {number} a
30+
* @returns {number}
31+
*/
32+
export function export_from_rust(a) {
33+
const ret = wasm.export_from_rust(a);
34+
return ret >>> 0;
35+
}
36+
37+
function isLikeNone(x) {
38+
return x === undefined || x === null;
39+
}
40+
41+
const RustStructFinalization = (typeof FinalizationRegistry === 'undefined')
42+
? { register: () => {}, unregister: () => {} }
43+
: new FinalizationRegistry(ptr => wasm.__wbg_ruststruct_free(ptr >>> 0, 1));
44+
45+
export class RustStruct {
46+
47+
__destroy_into_raw() {
48+
const ptr = this.__wbg_ptr;
49+
this.__wbg_ptr = 0;
50+
RustStructFinalization.unregister(this);
51+
return ptr;
52+
}
53+
54+
free() {
55+
const ptr = this.__destroy_into_raw();
56+
wasm.__wbg_ruststruct_free(ptr, 0);
57+
}
58+
/**
59+
* @returns {number}
60+
*/
61+
get foo() {
62+
const ret = wasm.__wbg_get_ruststruct_foo(this.__wbg_ptr);
63+
return ret >>> 0;
64+
}
65+
/**
66+
* @param {number} arg0
67+
*/
68+
set foo(arg0) {
69+
wasm.__wbg_set_ruststruct_foo(this.__wbg_ptr, arg0);
70+
}
71+
/**
72+
* @returns {number}
73+
*/
74+
get someCoolField() {
75+
const ret = wasm.__wbg_get_ruststruct_someCoolField(this.__wbg_ptr);
76+
return ret >>> 0;
77+
}
78+
/**
79+
* @param {number} arg0
80+
*/
81+
set someCoolField(arg0) {
82+
wasm.__wbg_set_ruststruct_someCoolField(this.__wbg_ptr, arg0);
83+
}
84+
/**
85+
* @returns {number}
86+
*/
87+
get another_field_for_you() {
88+
const ret = wasm.__wbg_get_ruststruct_another_field_for_you(this.__wbg_ptr);
89+
return ret >>> 0;
90+
}
91+
/**
92+
* @param {number} arg0
93+
*/
94+
set another_field_for_you(arg0) {
95+
wasm.__wbg_set_ruststruct_another_field_for_you(this.__wbg_ptr, arg0);
96+
}
97+
static i_dont_get_renamed() {
98+
wasm.ruststruct_i_dont_get_renamed();
99+
}
100+
/**
101+
* @param {number | undefined} [amount]
102+
*/
103+
incrementFoo(amount) {
104+
wasm.ruststruct_incrementFoo(this.__wbg_ptr, isLikeNone(amount) ? 0x100000001 : (amount) >>> 0);
105+
}
106+
/**
107+
* @param {number} foo
108+
*/
109+
setFoo(foo) {
110+
wasm.ruststruct_setFoo(this.__wbg_ptr, foo);
111+
}
112+
/**
113+
* @returns {number}
114+
*/
115+
get_another() {
116+
const ret = wasm.ruststruct_get_another(this.__wbg_ptr);
117+
return ret >>> 0;
118+
}
119+
}
120+
121+
export const __wbg_foobar_aa5072d28246f9cb = typeof foo_bar == 'function' ? foo_bar : notDefined('foo_bar');
122+
123+
export const __wbg_quxCorge_d8ec2d56c00b013f = typeof quxCorge == 'function' ? quxCorge : notDefined('quxCorge');
124+
125+
export const __wbg_yesNo_1feba4b061143a4c = typeof Baz.yesNo == 'function' ? Baz.yesNo : notDefined('Baz.yesNo');
126+
127+
export function __wbindgen_throw(arg0, arg1) {
128+
throw new Error(getStringFromWasm0(arg0, arg1));
129+
};
130+

crates/cli/tests/reference/rename.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use wasm_bindgen::prelude::*;
2+
3+
#[wasm_bindgen(rename_all = "camelCase")]
4+
extern "C" {
5+
#[wasm_bindgen(js_name = foo_bar)]
6+
fn foo_bar();
7+
fn qux_corge();
8+
#[wasm_bindgen(js_namespace = Baz)]
9+
fn yes_no();
10+
}
11+
12+
#[wasm_bindgen]
13+
pub fn export_from_rust(a: u32) -> u32 {
14+
foo_bar();
15+
qux_corge();
16+
yes_no();
17+
18+
a
19+
}
20+
21+
#[wasm_bindgen(rename_all = "camelCase")]
22+
pub struct RustStruct {
23+
pub foo: u32,
24+
pub some_cool_field: u32,
25+
#[wasm_bindgen(js_name = "another_field_for_you")]
26+
pub another_field: u32,
27+
}
28+
29+
#[wasm_bindgen]
30+
impl RustStruct {
31+
pub fn i_dont_get_renamed() {}
32+
}
33+
34+
#[wasm_bindgen(rename_all = "camelCase")]
35+
impl RustStruct {
36+
pub fn increment_foo(&mut self, amount: Option<u32>) {
37+
self.foo += amount.unwrap_or(1);
38+
}
39+
40+
pub fn set_foo(&mut self, foo: u32) {
41+
self.foo = foo;
42+
}
43+
44+
#[wasm_bindgen(js_name = get_another)]
45+
pub fn another(&self) -> u32 {
46+
self.another_field
47+
}
48+
}

crates/cli/tests/reference/rename.wat

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(module $reference_test.wasm
2+
(type (;0;) (func))
3+
(type (;1;) (func (param i32) (result i32)))
4+
(type (;2;) (func (param i32 i32)))
5+
(type (;3;) (func (param i32 f64)))
6+
(func $__wbg_get_ruststruct_foo (;0;) (type 1) (param i32) (result i32))
7+
(func $__wbg_get_ruststruct_someCoolField (;1;) (type 1) (param i32) (result i32))
8+
(func $__wbg_get_ruststruct_another_field_for_you (;2;) (type 1) (param i32) (result i32))
9+
(func $ruststruct_incrementFoo (;3;) (type 3) (param i32 f64))
10+
(func $__wbg_set_ruststruct_foo (;4;) (type 2) (param i32 i32))
11+
(func $__wbg_set_ruststruct_someCoolField (;5;) (type 2) (param i32 i32))
12+
(func $__wbg_set_ruststruct_another_field_for_you (;6;) (type 2) (param i32 i32))
13+
(func $ruststruct_get_another (;7;) (type 1) (param i32) (result i32))
14+
(func $ruststruct_setFoo (;8;) (type 2) (param i32 i32))
15+
(func $export_from_rust (;9;) (type 1) (param i32) (result i32))
16+
(func $__wbg_ruststruct_free (;10;) (type 2) (param i32 i32))
17+
(func $ruststruct_i_dont_get_renamed (;11;) (type 0))
18+
(memory (;0;) 17)
19+
(export "memory" (memory 0))
20+
(export "export_from_rust" (func $export_from_rust))
21+
(export "__wbg_ruststruct_free" (func $__wbg_ruststruct_free))
22+
(export "__wbg_get_ruststruct_foo" (func $__wbg_get_ruststruct_foo))
23+
(export "__wbg_set_ruststruct_foo" (func $__wbg_set_ruststruct_foo))
24+
(export "__wbg_get_ruststruct_someCoolField" (func $__wbg_get_ruststruct_someCoolField))
25+
(export "__wbg_set_ruststruct_someCoolField" (func $__wbg_set_ruststruct_someCoolField))
26+
(export "__wbg_get_ruststruct_another_field_for_you" (func $__wbg_get_ruststruct_another_field_for_you))
27+
(export "__wbg_set_ruststruct_another_field_for_you" (func $__wbg_set_ruststruct_another_field_for_you))
28+
(export "ruststruct_i_dont_get_renamed" (func $ruststruct_i_dont_get_renamed))
29+
(export "ruststruct_incrementFoo" (func $ruststruct_incrementFoo))
30+
(export "ruststruct_setFoo" (func $ruststruct_setFoo))
31+
(export "ruststruct_get_another" (func $ruststruct_get_another))
32+
(@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext")
33+
)
34+

crates/macro-support/src/lib.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,70 @@ pub fn expand_class_marker(
104104
Ok(tokens)
105105
}
106106

107+
/// Describes how to rename the fields, methods, and classes in the generated
108+
/// bindings.
109+
///
110+
/// This design heavily mimics the `#[serde(rename_all = "...")]` attribute.
111+
#[derive(Debug, Clone, Copy)]
112+
enum RenameRule {
113+
None,
114+
CamelCase,
115+
}
116+
impl RenameRule {
117+
fn snake_case_to_camel_case(name: String) -> String {
118+
let mut camel = String::new();
119+
let mut capitalize = false;
120+
for c in name.chars() {
121+
if c == '_' {
122+
capitalize = true;
123+
} else if capitalize {
124+
camel.push(c.to_ascii_uppercase());
125+
capitalize = false;
126+
} else {
127+
camel.push(c);
128+
}
129+
}
130+
camel
131+
}
132+
133+
fn rename_snake_case(self, name: String) -> String {
134+
match self {
135+
RenameRule::None => name.to_string(),
136+
RenameRule::CamelCase => Self::snake_case_to_camel_case(name),
137+
}
138+
}
139+
140+
/// Applies the rule to the field of a struct or enum variant data.
141+
fn apply_to_field(self, name: String) -> String {
142+
self.rename_snake_case(name)
143+
}
144+
/// Applies the rule to the method of a struct or enum.
145+
fn apply_to_method(self, name: String) -> String {
146+
self.rename_snake_case(name)
147+
}
148+
149+
fn rule_name(self) -> &'static str {
150+
match self {
151+
RenameRule::None => "none",
152+
RenameRule::CamelCase => "camelCase",
153+
}
154+
}
155+
}
156+
impl TryFrom<&str> for RenameRule {
157+
type Error = ();
158+
fn try_from(s: &str) -> Result<Self, Self::Error> {
159+
match s {
160+
"none" => Ok(RenameRule::None),
161+
"camelCase" => Ok(RenameRule::CamelCase),
162+
_ => Err(()),
163+
}
164+
}
165+
}
166+
107167
struct ClassMarker {
108168
class: syn::Ident,
109169
js_class: String,
170+
rename_all: RenameRule,
110171
wasm_bindgen: syn::Path,
111172
wasm_bindgen_futures: syn::Path,
112173
}
@@ -121,6 +182,11 @@ impl Parse for ClassMarker {
121182
.map(String::from)
122183
.unwrap_or(js_class);
123184

185+
input.parse::<Option<Token![,]>>()?;
186+
187+
let rename_all = input.parse::<syn::LitStr>()?.value();
188+
let rename_all: RenameRule = rename_all[..].try_into().unwrap();
189+
124190
let mut wasm_bindgen = None;
125191
let mut wasm_bindgen_futures = None;
126192

@@ -162,6 +228,7 @@ impl Parse for ClassMarker {
162228
Ok(ClassMarker {
163229
class,
164230
js_class,
231+
rename_all,
165232
wasm_bindgen: wasm_bindgen.unwrap_or_else(|| syn::parse_quote! { wasm_bindgen }),
166233
wasm_bindgen_futures: wasm_bindgen_futures
167234
.unwrap_or_else(|| syn::parse_quote! { wasm_bindgen_futures }),

0 commit comments

Comments
 (0)