Skip to content

Commit

Permalink
feat(wasm): add export, append_fetcher macros ; remove `api_metho…
Browse files Browse the repository at this point in the history
…d` macro
  • Loading branch information
Vexcited committed Dec 30, 2024
1 parent 095f985 commit f70ea97
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 54 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
syn = { version = "2.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"
convert_case = "0.6.0"

[dev-dependencies]
wasm-bindgen = "0.2"
Expand Down
16 changes: 0 additions & 16 deletions wasm/examples/export_method.rs

This file was deleted.

128 changes: 90 additions & 38 deletions wasm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,62 +1,114 @@
extern crate proc_macro;

use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, FnArg, Ident, ItemFn};
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, parse_quote, Attribute, DeriveInput, FnArg, ItemFn};
use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use quote::quote;

struct ApiMethodArgs {
js_name: Option<String>,
}

impl Parse for ApiMethodArgs {
fn parse(input: ParseStream) -> Result<Self> {
if input.is_empty() {
return Ok(ApiMethodArgs { js_name: None });
}

let ident = input.parse::<Ident>()?;
Ok(ApiMethodArgs { js_name: Some(ident.to_string()) })
/// This macro adds the `#[wasm_bindgen]` attribute.
///
/// If applied to a function, it will also rename the
/// name to camel case using `js_name`.
///
/// # Usages
///
/// ```rust
/// // The method will be called `retrieveCas` in the generated bindings.
/// #[cfg_attr(target_arch = "wasm32", wasm::export)]
/// pub async fn retrieve_cas() -> Result<String, Error> {
/// // ...
/// }
///
/// #[cfg_attr(target_arch = "wasm32", wasm::export)]
/// pub struct Session {
/// // ...
/// }
///
/// #[cfg_attr(target_arch = "wasm32", wasm::export)]
/// impl Session {
/// // ...
/// }
///
#[proc_macro_attribute]
pub fn export(_args: TokenStream, input: TokenStream) -> TokenStream {
// let input_clone = input.clone();
let input = parse_macro_input!(input as syn::Item);

match input {
syn::Item::Fn(mut input) => {
let vis = &input.vis;
let sig = &input.sig;
let block = &input.block;
let attrs = &mut input.attrs;

let name = sig.ident.to_string();
let camel_case_name = name.to_case(Case::Camel);

let wasm_bindgen_attr: Attribute = parse_quote!(#[wasm_bindgen::prelude::wasm_bindgen(js_name = #camel_case_name)]);
attrs.push(wasm_bindgen_attr);

let output = quote! {
#(#attrs)*
#vis #sig {
#block
}
};

TokenStream::from(output)
},
syn::Item::Struct(mut input) => {
let vis = &input.vis;
let ident = &input.ident;
let fields = &input.fields;
let attrs = &mut input.attrs;

let wasm_bindgen_attr: Attribute = parse_quote!(#[wasm_bindgen::prelude::wasm_bindgen]);
attrs.push(wasm_bindgen_attr);

let output = quote! {
#(#attrs)*
#vis struct #ident {
#fields
}
};

TokenStream::from(output)
},
syn::Item::Impl(mut input) => {
let attrs = &mut input.attrs;

let wasm_bindgen_attr: Attribute = parse_quote!(#[wasm_bindgen::prelude::wasm_bindgen]);
attrs.push(wasm_bindgen_attr);

let output = quote! {
#(#attrs)*
impl #input
};

TokenStream::from(output)
},
_ => panic!("Only functions, structs and impls are supported"),
}
}

/// This macro adds the `#[wasm_bindgen]` attribute to the function
/// and adds a `fetcher: js_sys::Function` parameter to the function signature.
/// This macro adds a `fetcher: js_sys::Function` parameter to the function signature.
///
/// ## Usages
///
/// ```rust
/// // the method will be called `retrieveCAS` in the generated bindings
/// #[cfg_attr(target_arch = "wasm32", wasm::api_method(retrieveCAS))]
/// pub async fn retrieve_cas() -> Result<String, Error> {
/// // a `fetcher` variable is available
/// // if the target architecture is `wasm32`
/// }
///
/// // the method will be still called `update` in the generated bindings
/// #[cfg_attr(target_arch = "wasm32", wasm::api_method)]
/// #[cfg_attr(target_arch = "wasm32", wasm::append_fetcher)]
/// pub async fn update() -> Result<String, Error> {
/// // a `fetcher` variable is available
/// // A `fetcher` variable is available
/// // if the target architecture is `wasm32`
/// }
/// ```
#[proc_macro_attribute]
pub fn api_method(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args with ApiMethodArgs::parse);
pub fn append_fetcher(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as ItemFn);
let vis = &input.vis;
let sig = &mut input.sig;
let block = &input.block;
let attrs = &mut input.attrs;

let wasm_bindgen_attr: Attribute = if let Some(name) = args.js_name {
parse_quote!(#[wasm_bindgen::prelude::wasm_bindgen(js_name = #name)])
}
else {
parse_quote!(#[wasm_bindgen::prelude::wasm_bindgen])
};

attrs.push(wasm_bindgen_attr);
let attrs = &input.attrs;

let fetcher_param: FnArg = parse_quote!(fetcher: js_sys::Function);
sig.inputs.push(fetcher_param);
Expand Down

0 comments on commit f70ea97

Please sign in to comment.