Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rename_all = "camelCase" option to automatically rename methods and functions #4215

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,6 @@ pub struct Function {
pub name: String,
/// The span of the function's name in Rust code
pub name_span: Span,
/// Whether the function has a js_name attribute
pub renamed_via_js_name: bool,
/// The arguments to the function
pub arguments: Vec<syn::PatType>,
/// The return type of the function, if provided
Expand Down
13 changes: 13 additions & 0 deletions crates/cli/tests/reference/rename.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* tslint:disable */
/* eslint-disable */
export function export_from_rust(a: number): number;
export class RustStruct {
free(): void;
static i_dont_get_renamed(): void;
incrementFoo(amount?: number): void;
setFoo(foo: number): void;
get_another(): number;
another_field_for_you: number;
foo: number;
someCoolField: number;
}
130 changes: 130 additions & 0 deletions crates/cli/tests/reference/rename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
let wasm;
export function __wbg_set_wasm(val) {
wasm = val;
}


function notDefined(what) { return () => { throw new Error(`${what} is not defined`); }; }

const lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;

let cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });

cachedTextDecoder.decode();

let cachedUint8ArrayMemory0 = null;

function getUint8ArrayMemory0() {
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
}
return cachedUint8ArrayMemory0;
}

function getStringFromWasm0(ptr, len) {
ptr = ptr >>> 0;
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
/**
* @param {number} a
* @returns {number}
*/
export function export_from_rust(a) {
const ret = wasm.export_from_rust(a);
return ret >>> 0;
}

function isLikeNone(x) {
return x === undefined || x === null;
}

const RustStructFinalization = (typeof FinalizationRegistry === 'undefined')
? { register: () => {}, unregister: () => {} }
: new FinalizationRegistry(ptr => wasm.__wbg_ruststruct_free(ptr >>> 0, 1));

export class RustStruct {

__destroy_into_raw() {
const ptr = this.__wbg_ptr;
this.__wbg_ptr = 0;
RustStructFinalization.unregister(this);
return ptr;
}

free() {
const ptr = this.__destroy_into_raw();
wasm.__wbg_ruststruct_free(ptr, 0);
}
/**
* @returns {number}
*/
get foo() {
const ret = wasm.__wbg_get_ruststruct_foo(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @param {number} arg0
*/
set foo(arg0) {
wasm.__wbg_set_ruststruct_foo(this.__wbg_ptr, arg0);
}
/**
* @returns {number}
*/
get someCoolField() {
const ret = wasm.__wbg_get_ruststruct_someCoolField(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @param {number} arg0
*/
set someCoolField(arg0) {
wasm.__wbg_set_ruststruct_someCoolField(this.__wbg_ptr, arg0);
}
/**
* @returns {number}
*/
get another_field_for_you() {
const ret = wasm.__wbg_get_ruststruct_another_field_for_you(this.__wbg_ptr);
return ret >>> 0;
}
/**
* @param {number} arg0
*/
set another_field_for_you(arg0) {
wasm.__wbg_set_ruststruct_another_field_for_you(this.__wbg_ptr, arg0);
}
static i_dont_get_renamed() {
wasm.ruststruct_i_dont_get_renamed();
}
/**
* @param {number | undefined} [amount]
*/
incrementFoo(amount) {
wasm.ruststruct_incrementFoo(this.__wbg_ptr, isLikeNone(amount) ? 0x100000001 : (amount) >>> 0);
}
/**
* @param {number} foo
*/
setFoo(foo) {
wasm.ruststruct_setFoo(this.__wbg_ptr, foo);
}
/**
* @returns {number}
*/
get_another() {
const ret = wasm.ruststruct_get_another(this.__wbg_ptr);
return ret >>> 0;
}
}

export const __wbg_foobar_aa5072d28246f9cb = typeof foo_bar == 'function' ? foo_bar : notDefined('foo_bar');

export const __wbg_quxCorge_d8ec2d56c00b013f = typeof quxCorge == 'function' ? quxCorge : notDefined('quxCorge');

export const __wbg_yesNo_1feba4b061143a4c = typeof Baz.yesNo == 'function' ? Baz.yesNo : notDefined('Baz.yesNo');

export function __wbindgen_throw(arg0, arg1) {
throw new Error(getStringFromWasm0(arg0, arg1));
};

48 changes: 48 additions & 0 deletions crates/cli/tests/reference/rename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use wasm_bindgen::prelude::*;

#[wasm_bindgen(rename_all = "camelCase")]
extern "C" {
#[wasm_bindgen(js_name = foo_bar)]
fn foo_bar();
fn qux_corge();
#[wasm_bindgen(js_namespace = Baz)]
fn yes_no();
}

#[wasm_bindgen]
pub fn export_from_rust(a: u32) -> u32 {
foo_bar();
qux_corge();
yes_no();

a
}

#[wasm_bindgen(rename_all = "camelCase")]
pub struct RustStruct {
pub foo: u32,
pub some_cool_field: u32,
#[wasm_bindgen(js_name = "another_field_for_you")]
pub another_field: u32,
}

#[wasm_bindgen]
impl RustStruct {
pub fn i_dont_get_renamed() {}
}

#[wasm_bindgen(rename_all = "camelCase")]
impl RustStruct {
pub fn increment_foo(&mut self, amount: Option<u32>) {
self.foo += amount.unwrap_or(1);
}

pub fn set_foo(&mut self, foo: u32) {
self.foo = foo;
}

#[wasm_bindgen(js_name = get_another)]
pub fn another(&self) -> u32 {
self.another_field
}
}
34 changes: 34 additions & 0 deletions crates/cli/tests/reference/rename.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
(module $reference_test.wasm
(type (;0;) (func))
(type (;1;) (func (param i32) (result i32)))
(type (;2;) (func (param i32 i32)))
(type (;3;) (func (param i32 f64)))
(func $__wbg_get_ruststruct_foo (;0;) (type 1) (param i32) (result i32))
(func $__wbg_get_ruststruct_someCoolField (;1;) (type 1) (param i32) (result i32))
(func $__wbg_get_ruststruct_another_field_for_you (;2;) (type 1) (param i32) (result i32))
(func $ruststruct_incrementFoo (;3;) (type 3) (param i32 f64))
(func $__wbg_set_ruststruct_foo (;4;) (type 2) (param i32 i32))
(func $__wbg_set_ruststruct_someCoolField (;5;) (type 2) (param i32 i32))
(func $__wbg_set_ruststruct_another_field_for_you (;6;) (type 2) (param i32 i32))
(func $ruststruct_get_another (;7;) (type 1) (param i32) (result i32))
(func $ruststruct_setFoo (;8;) (type 2) (param i32 i32))
(func $export_from_rust (;9;) (type 1) (param i32) (result i32))
(func $__wbg_ruststruct_free (;10;) (type 2) (param i32 i32))
(func $ruststruct_i_dont_get_renamed (;11;) (type 0))
(memory (;0;) 17)
(export "memory" (memory 0))
(export "export_from_rust" (func $export_from_rust))
(export "__wbg_ruststruct_free" (func $__wbg_ruststruct_free))
(export "__wbg_get_ruststruct_foo" (func $__wbg_get_ruststruct_foo))
(export "__wbg_set_ruststruct_foo" (func $__wbg_set_ruststruct_foo))
(export "__wbg_get_ruststruct_someCoolField" (func $__wbg_get_ruststruct_someCoolField))
(export "__wbg_set_ruststruct_someCoolField" (func $__wbg_set_ruststruct_someCoolField))
(export "__wbg_get_ruststruct_another_field_for_you" (func $__wbg_get_ruststruct_another_field_for_you))
(export "__wbg_set_ruststruct_another_field_for_you" (func $__wbg_set_ruststruct_another_field_for_you))
(export "ruststruct_i_dont_get_renamed" (func $ruststruct_i_dont_get_renamed))
(export "ruststruct_incrementFoo" (func $ruststruct_incrementFoo))
(export "ruststruct_setFoo" (func $ruststruct_setFoo))
(export "ruststruct_get_another" (func $ruststruct_get_another))
(@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext")
)

67 changes: 67 additions & 0 deletions crates/macro-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,70 @@ pub fn expand_class_marker(
Ok(tokens)
}

/// Describes how to rename the fields, methods, and classes in the generated
/// bindings.
///
/// This design heavily mimics the `#[serde(rename_all = "...")]` attribute.
#[derive(Debug, Clone, Copy)]
enum RenameRule {
None,
CamelCase,
}
impl RenameRule {
fn snake_case_to_camel_case(name: String) -> String {
let mut camel = String::new();
let mut capitalize = false;
for c in name.chars() {
if c == '_' {
capitalize = true;
} else if capitalize {
camel.push(c.to_ascii_uppercase());
daxpedda marked this conversation as resolved.
Show resolved Hide resolved
capitalize = false;
} else {
camel.push(c);
}
}
camel
}

fn rename_snake_case(self, name: String) -> String {
match self {
RenameRule::None => name.to_string(),
RenameRule::CamelCase => Self::snake_case_to_camel_case(name),
}
}

/// Applies the rule to the field of a struct or enum variant data.
fn apply_to_field(self, name: String) -> String {
self.rename_snake_case(name)
}
/// Applies the rule to the method of a struct or enum.
fn apply_to_method(self, name: String) -> String {
self.rename_snake_case(name)
}
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved

fn rule_name(self) -> &'static str {
match self {
RenameRule::None => "none",
RenameRule::CamelCase => "camelCase",
}
}
}
impl TryFrom<&str> for RenameRule {
type Error = ();
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"none" => Ok(RenameRule::None),
"camelCase" => Ok(RenameRule::CamelCase),
_ => Err(()),
}
}
}

struct ClassMarker {
class: syn::Ident,
js_class: String,
rename_all: RenameRule,
wasm_bindgen: syn::Path,
wasm_bindgen_futures: syn::Path,
}
Expand All @@ -121,6 +182,11 @@ impl Parse for ClassMarker {
.map(String::from)
.unwrap_or(js_class);

input.parse::<Option<Token![,]>>()?;

let rename_all = input.parse::<syn::LitStr>()?.value();
let rename_all: RenameRule = rename_all[..].try_into().unwrap();

let mut wasm_bindgen = None;
let mut wasm_bindgen_futures = None;

Expand Down Expand Up @@ -162,6 +228,7 @@ impl Parse for ClassMarker {
Ok(ClassMarker {
class,
js_class,
rename_all,
wasm_bindgen: wasm_bindgen.unwrap_or_else(|| syn::parse_quote! { wasm_bindgen }),
wasm_bindgen_futures: wasm_bindgen_futures
.unwrap_or_else(|| syn::parse_quote! { wasm_bindgen_futures }),
Expand Down
Loading
Loading