use std::str::FromStr;
use original::{Style, VariantStyle};
use quote::ToTokens;
use regex::{Captures, Regex};
use syn::{Attribute, ImplItem, ItemImpl};
mod original;
mod direct_impl;
#[proc_macro_derive(Lift, attributes(component))]
pub fn lift(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
replace_namespace(original::lift(input))
}
#[proc_macro_derive(Lower, attributes(component))]
pub fn lower(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
replace_namespace(original::lower(input))
}
#[proc_macro_derive(ComponentType, attributes(component))]
pub fn component_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
replace_namespace(original::component_type(input))
}
#[proc_macro]
pub fn flags_sys(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
replace_namespace(original::flags(input, true))
}
#[proc_macro]
pub fn flags_js(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
replace_namespace(original::flags(input, false))
}
fn bindgen(input: proc_macro::TokenStream) -> String {
let as_string = replace_namespace_str(original::bindgen(input));
let regex = Regex::new("derive\\(([^\\)]*Clone[^\\)]*)\\)").unwrap();
let as_string = regex.replace_all(&as_string, |caps: &Captures| {
if caps[0].contains("PartialEq") {
caps[0].to_string()
} else {
format!("derive({}, PartialEq)", &caps[1])
}
});
as_string.to_string()
}
#[proc_macro]
pub fn bindgen_sys(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let as_string = bindgen(input);
proc_macro::TokenStream::from_str(&as_string).unwrap()
}
#[proc_macro]
pub fn bindgen_js(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let as_string = bindgen(input);
let regex = Regex::new("\\*\\s*__exports\\.typed_func([^?]*)\\?\\.func\\(\\)").unwrap();
let as_string = regex.replace_all(&as_string, "__exports.typed_func$1?.func().clone()");
let regex = Regex::new("new_unchecked\\(self\\.([^)]*)\\)").unwrap();
let as_string = regex.replace_all(&as_string, "new_unchecked(self.$1.clone())");
let regex = Regex::new("add_to_linker\\s*<\\s*T").unwrap();
let as_string = regex.replace_all(&as_string, "add_to_linker<T: 'static");
let regex = Regex::new("add_root_to_linker\\s*<\\s*T").unwrap();
let as_string = regex.replace_all(&as_string, "add_root_to_linker<T: 'static");
let regex = Regex::new("#\\[derive[^C]*ComponentType\\s*\\)\\s*\\]").unwrap();
let as_string = regex.replace_all(&as_string, "");
let regex =
Regex::new("const\\s*_\\s*:\\s*\\(\\)\\s*=[^}]*ComponentType[^}]*\\}\\s*;").unwrap();
let as_string = regex.replace_all(&as_string, "");
let regex = Regex::new("#\\[derive\\([^)]*Lift\\)\\]").unwrap();
let as_string = regex.replace_all(&as_string, "#[derive(wasm_bridge::component::SizeDescription)]\n#[derive(wasm_bridge::component::LiftJs)]");
let regex = Regex::new("#\\[derive\\([^)]*Lower\\)\\]").unwrap();
let as_string = regex.replace_all(&as_string, "#[derive(wasm_bridge::component::LowerJs)]");
proc_macro::TokenStream::from_str(&as_string).unwrap()
}
#[proc_macro_derive(SizeDescription, attributes(component))]
pub fn derive_size_description(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input: syn::DeriveInput = syn::parse(input).unwrap();
let name = derive_input.ident;
let struct_style = style_from_attributes(&derive_input.attrs);
let tokens = match derive_input.data {
syn::Data::Struct(data) => direct_impl::size_description_struct(name, data),
syn::Data::Enum(data) => match struct_style.expect("cannot find attribute style") {
Style::Record => unreachable!("enum is not a record"),
Style::Variant(VariantStyle::Enum) => direct_impl::size_description_enum(name, data),
Style::Variant(VariantStyle::Variant) => {
direct_impl::size_description_variant(name, data)
}
},
syn::Data::Union(_) => unimplemented!("Union type should not be generated by wit bindgen"),
};
proc_macro::TokenStream::from(tokens)
}
#[proc_macro_derive(LiftJs, attributes(component))]
pub fn derive_lift(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input: syn::DeriveInput = syn::parse(input).unwrap();
let name = derive_input.ident;
let struct_style = style_from_attributes(&derive_input.attrs);
let tokens = match derive_input.data {
syn::Data::Struct(data) => direct_impl::lift_struct(name, data),
syn::Data::Enum(data) => match struct_style.expect("cannot find attribute style") {
Style::Record => unreachable!("enum is not a record"),
Style::Variant(VariantStyle::Enum) => direct_impl::lift_enum(name, data),
Style::Variant(VariantStyle::Variant) => direct_impl::lift_variant(name, data),
},
syn::Data::Union(_) => unimplemented!("Union type should not be generated by wit bindgen"),
};
proc_macro::TokenStream::from(tokens)
}
#[proc_macro_derive(LowerJs, attributes(component))]
pub fn derive_lower(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input: syn::DeriveInput = syn::parse(input).unwrap();
let name = derive_input.ident;
let struct_style = style_from_attributes(&derive_input.attrs);
let tokens = match derive_input.data {
syn::Data::Struct(data) => direct_impl::lower_struct(name, data),
syn::Data::Enum(data) => match struct_style.expect("cannot find attribute style") {
Style::Record => unreachable!("enum is not a record"),
Style::Variant(VariantStyle::Enum) => direct_impl::lower_enum(name, data),
Style::Variant(VariantStyle::Variant) => direct_impl::lower_variant(name, data),
},
syn::Data::Union(_) => unimplemented!("Union type should not be generated by wit bindgen"),
};
proc_macro::TokenStream::from(tokens)
}
#[proc_macro_attribute]
pub fn async_trait(
_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut item_impl: ItemImpl = syn::parse(input).unwrap();
for item in item_impl.items.iter_mut() {
if let ImplItem::Fn(method) = item {
method.sig.asyncness = None;
}
}
item_impl.into_token_stream().into()
}
fn replace_namespace_str(stream: proc_macro::TokenStream) -> String {
let as_string = stream.to_string();
let regex = Regex::new("wasmtime[^:]*::").unwrap();
let as_string = regex.replace_all(&as_string, "wasm_bridge::");
as_string.to_string()
}
fn replace_namespace(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
let as_string = replace_namespace_str(stream);
proc_macro::TokenStream::from_str(&as_string).unwrap()
}
fn style_from_attributes(attributes: &[Attribute]) -> Option<Style> {
attributes
.iter()
.find(|attr| attr.path().is_ident("component"))
.map(|attr| {
attr.parse_args()
.expect("Failed to parse Style from Attribute")
})
}
#[proc_macro]
pub fn size_description_tuple(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
direct_impl::size_description_tuple(tokens)
}