use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, ItemFn, ItemStruct, ItemTrait};
#[proc_macro_attribute]
pub fn patch_trait(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemTrait);
let trait_name = &input.ident;
let vis = &input.vis;
let trait_items = &input.items;
let generics = &input.generics;
let where_clause = &input.generics.where_clause;
let expanded = quote! {
#vis trait #trait_name #generics #where_clause {
#(#trait_items)*
}
impl #trait_name {
pub const fn __dynpatch_trait_version() -> (u32, u32, u32) {
(0, 1, 0)
}
pub const fn __dynpatch_trait_name() -> &'static str {
stringify!(#trait_name)
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn patchable(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let vis = &input.vis;
let inputs = &input.sig.inputs;
let output = &input.sig.output;
let block = &input.block;
let attrs = &input.attrs;
let expanded = quote! {
#(#attrs)*
#vis fn #fn_name(#inputs) #output {
#block
}
const _: () = {
#[used]
#[cfg_attr(target_os = "linux", link_section = ".dynpatch")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__dynpatch")]
#[cfg_attr(target_os = "windows", link_section = ".dynpatch")]
static __DYNPATCH_METADATA: &str = concat!(
"patchable:",
stringify!(#fn_name)
);
};
};
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn patch_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemStruct);
let struct_name = &input.ident;
let vis = &input.vis;
let fields = &input.fields;
let expanded = quote! {
#vis struct #struct_name #fields
#[no_mangle]
pub extern "C" fn __dynpatch_metadata() -> dynpatch_interface::PatchMetadata {
dynpatch_interface::PatchMetadata::new(
stringify!(#struct_name).to_string(),
dynpatch_interface::Version::new(0, 1, 0),
dynpatch_interface::Version::new(0, 1, 0),
dynpatch_interface::compute_type_hash(
stringify!(#struct_name),
core::mem::size_of::<#struct_name>(),
core::mem::align_of::<#struct_name>(),
),
)
}
#[no_mangle]
pub extern "C" fn __dynpatch_create() -> Box<#struct_name> {
Box::new(#struct_name::default())
}
};
TokenStream::from(expanded)
}
#[proc_macro_attribute]
pub fn patch_entry(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let fn_name = &input.sig.ident;
let vis = &input.vis;
let block = &input.block;
let expanded = quote! {
#vis fn #fn_name() -> Result<(), String> {
#block
}
#[no_mangle]
pub extern "C" fn __dynpatch_entry() -> i32 {
match #fn_name() {
Ok(()) => 0,
Err(_) => -1,
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(HotConfig)]
pub fn derive_hot_config(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let expanded = quote! {
impl #name {
pub fn __dynpatch_type_hash() -> u64 {
dynpatch_interface::compute_type_hash(
stringify!(#name),
core::mem::size_of::<#name>(),
core::mem::align_of::<#name>(),
)
}
}
};
TokenStream::from(expanded)
}