extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::{ItemEnum, ItemStruct, ItemType};
fn get_name(item: &proc_macro2::TokenStream) -> Option<Ident> {
let ident = if let Ok(ItemStruct { ident, .. }) = syn::parse2(item.clone()) {
ident
} else if let Ok(ItemEnum { ident, .. }) = syn::parse2(item.clone()) {
ident
} else if let Ok(ItemType { ident, .. }) = syn::parse2(item.clone()) {
ident
} else {
return None;
};
Some(ident)
}
#[allow(clippy::too_many_lines)]
fn stateroom_wasm_impl(item: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
let name =
get_name(item).expect("Can only use #[stateroom_wasm] on a struct, enum, or type alias.");
quote! {
#item
mod _stateroom_wasm_macro_autogenerated {
extern crate alloc;
use super::#name;
mod ffi {
extern "C" {
pub fn stateroom_send(message_ptr: *const u8, message_len: u32);
}
}
static mut SERVER_STATE: Option<stateroom_wasm::WrappedStateroomService<#name>> = None;
#[no_mangle]
pub static STATEROOM_API_VERSION: i32 = 1;
#[no_mangle]
pub static STATEROOM_API_PROTOCOL: i32 = 0;
#[no_mangle]
extern "C" fn stateroom_recv(message_ptr: *const u8, message_len: u32) {
let state = unsafe {
match SERVER_STATE.as_mut() {
Some(s) => s,
None => {
let s = stateroom_wasm::WrappedStateroomService::new(#name::default(), ffi::stateroom_send);
SERVER_STATE.replace(s);
SERVER_STATE.as_mut().unwrap()
}
}
};
state.recv(message_ptr, message_len);
}
#[no_mangle]
pub unsafe extern "C" fn stateroom_malloc(size: u32) -> *mut u8 {
if size == 0 {
return core::ptr::null_mut();
}
let layout = core::alloc::Layout::from_size_align_unchecked(size as usize, 0);
alloc::alloc::alloc(layout)
}
#[no_mangle]
pub unsafe extern "C" fn stateroom_free(ptr: *mut u8, size: u32) {
if size == 0 {
return;
}
let layout = core::alloc::Layout::from_size_align_unchecked(size as usize, 0);
alloc::alloc::dealloc(ptr, layout);
}
}
}
}
#[proc_macro_attribute]
pub fn stateroom_wasm(_attr: TokenStream, item: TokenStream) -> TokenStream {
#[allow(clippy::needless_borrow)]
stateroom_wasm_impl(&item.into()).into()
}
#[cfg(test)]
mod test {
use super::get_name;
use quote::quote;
#[test]
fn test_parse_name() {
assert_eq!(
"MyStruct",
get_name("e! {
struct MyStruct {}
})
.unwrap()
.to_string()
);
assert_eq!(
"AnotherStruct",
get_name("e! {
struct AnotherStruct;
})
.unwrap()
.to_string()
);
assert_eq!(
"ATupleStruct",
get_name("e! {
struct ATupleStruct(u32, u32, u32);
})
.unwrap()
.to_string()
);
assert_eq!(
"AnEnum",
get_name("e! {
enum AnEnum {
Option1,
Option2(u32),
}
})
.unwrap()
.to_string()
);
assert_eq!(
"ATypeDecl",
get_name("e! {
type ATypeDecl = u32;
})
.unwrap()
.to_string()
);
assert!(get_name("e! {
impl Foo {}
})
.is_none());
}
}