dynex 0.1.1

Inherit and derive object-unsafe traits for dynamic Rust.
Documentation
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};

pub fn main(input: TokenStream) -> TokenStream {
    let mut item: syn::ItemTrait = syn::parse2(input).expect("expect trait");
    let ident = &item.ident;
    let mut impls = vec![];
    for param in &mut item.supertraits {
        let syn::TypeParamBound::Trait(bound) = param else {
            continue;
        };
        let op = bound.path.to_token_stream().to_string();
        match op.as_str() {
            "Clone" => {
                bound.path = syn::parse_quote! { dynex_core::DynClone };
                impls.push(quote! {
                    impl Clone for Box<dyn #ident> {
                        fn clone(&self) -> Self {
                            dynex_core::ptr::convert_to_box(self, dynex_core::DynClone::dyn_clone)
                        }
                    }
                });
            },
            "PartialEq" | "PartialOrd" => {
                let name = format_ident!("{}", op);
                let (method, dyn_method, return_type) = match op.as_str() {
                    "PartialEq" => (quote!(eq), quote!(dyn_eq), quote!(bool)),
                    "PartialOrd" => (quote!(partial_cmp), quote!(dyn_partial_cmp), quote!(Option<core::cmp::Ordering>)),
                    _ => unreachable!(),
                };
                bound.path = syn::parse_quote! { dynex_core::cmp::#name };
                impls.push(quote! {
                    impl core::cmp::#name for dyn #ident {
                        fn #method(&self, other: &Self) -> #return_type {
                            self.#dyn_method(other.as_any())
                        }
                    }
                });
            },
            "Neg" | "Not" => {
                let name = format_ident!("{}", op);
                let method = format_ident!("{}", op.to_lowercase());
                let dyn_method = format_ident!("dyn_{}", method);
                bound.path = syn::parse_quote! { dynex_core::ops::#name };
                impls.push(quote! {
                    impl std::ops::#name for Box<dyn #ident> {
                        type Output = Self;
                        fn #method(self) -> Self {
                            dynex_core::ptr::convert_into_box(self, |m| m.#dyn_method())
                        }
                    }
                });
            },
            "Add" | "Sub" | "Mul" | "Div" | "Rem" |
            "BitAnd" | "BitOr" | "BitXor" | "Shl" | "Shr" => {
                let name = format_ident!("{}", op);
                let method = format_ident!("{}", op.to_lowercase());
                let dyn_method = format_ident!("dyn_{}", method);
                bound.path = syn::parse_quote! { dynex_core::ops::#name };
                impls.push(quote! {
                    impl std::ops::#name for Box<dyn #ident> {
                        type Output = Self;
                        fn #method(self, other: Self) -> Self {
                            dynex_core::ptr::convert_into_box(self, |m| m.#dyn_method(other.as_any_box()))
                        }
                    }
                });
            },
            "AddAssign" | "SubAssign" | "MulAssign" | "DivAssign" | "RemAssign" |
            "BitAndAssign" | "BitOrAssign" | "BitXorAssign" | "ShlAssign" | "ShrAssign" => {
                let name = format_ident!("{}", op);
                let method = format_ident!("{}_assign", op[0..op.len() - 6].to_lowercase());
                let dyn_method = format_ident!("dyn_{}_assign", method);
                bound.path = syn::parse_quote! { dynex_core::ops::#name };
                impls.push(quote! {
                    impl std::ops::#name for Box<dyn #ident> {
                        fn #method(&mut self, other: Self) {
                            self.#dyn_method(other.as_any_box())
                        }
                    }
                });
            },
            _ => {},
        }
    }
    quote! {
        #item
        #(#impls)*
    }
}