1#![feature(map_try_insert)]
5
6use cargo_manifest_proc_macros::CargoManifest;
7use proc_macro::TokenStream as TokenStream1;
8use proc_macro2::TokenStream as TokenStream2;
9use quote::quote;
10use syn::{
11 Error, ItemEnum, ItemStruct, Token, TypePath,
12 parse::{self, Parse, ParseStream},
13 parse_macro_input,
14 punctuated::Punctuated,
15};
16use tracing_proc_macros_ink::proc_macro_logger_default_setup;
17
18struct TraitCastTargets {
20 targets: Vec<TypePath>,
21}
22
23impl Parse for TraitCastTargets {
24 fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
25 let targets: Vec<TypePath> = Punctuated::<TypePath, Token![,]>::parse_terminated(input)?
26 .into_iter()
27 .collect();
28 Ok(Self { targets })
29 }
30}
31
32impl quote::ToTokens for TraitCastTargets {
33 fn to_tokens(&self, tokens: &mut TokenStream2) {
34 let vars = &self.targets;
35 tokens.extend(quote!(#(#vars),*));
36 }
37}
38
39#[proc_macro_attribute]
71pub fn make_trait_castable(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
72 proc_macro_logger_default_setup();
73
74 let crate_path = CargoManifest::shared().resolve_crate_path("trait-cast", &[]);
75
76 let input = TokenStream2::from(input);
78
79 let trait_cast_targets = parse_macro_input!(args as TraitCastTargets);
80
81 let input_struct = syn::parse2::<ItemStruct>(input.clone());
83 let mut source_ident = input_struct.map(|item_struct| item_struct.ident);
84
85 if source_ident.is_err() {
87 let input_enum = syn::parse2::<ItemEnum>(input.clone());
88 source_ident = input_enum.map(|item_enum| item_enum.ident);
89 }
90
91 if let Err(err) = source_ident {
92 let mut custom_error_message = Error::new(err.span(), "Expected a struct or enum");
93 custom_error_message.combine(err);
94 return custom_error_message.to_compile_error().into();
95 }
96
97 let source_ident = source_ident.unwrap();
98
99 TokenStream1::from(quote!(
100 #input
101 #crate_path::make_trait_castable_decl! {
102 #source_ident => (#trait_cast_targets)
103 }))
104}