55 lines
1.4 KiB
Rust
55 lines
1.4 KiB
Rust
use crate::helper_structs::Pair;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use syn::{DeriveInput, Expr, Type};
|
|
|
|
pub fn derive_transitive_child_impl(input_item: TokenStream) -> syn::Result<TokenStream> {
|
|
let input = syn::parse2::<DeriveInput>(input_item).unwrap();
|
|
|
|
let attribute = input
|
|
.attrs
|
|
.iter()
|
|
.find(|a| a.path().is_ident("parent"))
|
|
.ok_or_else(|| syn::Error::new(Span::call_site(), format!("tried to derive TransitiveChild without a #[parent] attribute (on {})", input.ident)))?;
|
|
|
|
let parent_is_top = input.attrs.iter().any(|a| a.path().is_ident("parent_is_top"));
|
|
|
|
let Pair {
|
|
first: parent_type,
|
|
second: to_parent,
|
|
..
|
|
} = attribute.parse_args::<Pair<Type, Expr>>()?;
|
|
|
|
let top_parent_type: Type = syn::parse_quote! { <#parent_type as TransitiveChild>::TopParent };
|
|
|
|
let input_type = &input.ident;
|
|
|
|
let trait_impl = quote::quote! {
|
|
impl TransitiveChild for #input_type {
|
|
type Parent = #parent_type;
|
|
type TopParent = #top_parent_type;
|
|
}
|
|
};
|
|
|
|
let from_for_parent = quote::quote! {
|
|
impl From<#input_type> for #parent_type {
|
|
fn from(x: #input_type) -> #parent_type {
|
|
(#to_parent)(x)
|
|
}
|
|
}
|
|
};
|
|
|
|
let from_for_top = quote::quote! {
|
|
impl From<#input_type> for #top_parent_type {
|
|
fn from(x: #input_type) -> #top_parent_type {
|
|
#top_parent_type::from((#to_parent)(x))
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(if parent_is_top {
|
|
quote::quote! { #trait_impl #from_for_parent }
|
|
} else {
|
|
quote::quote! { #trait_impl #from_for_parent #from_for_top }
|
|
})
|
|
}
|