trait_cast_macros/
derive_traitcastable_any.rs1use cargo_manifest_proc_macros::CargoManifest;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{
5 DeriveInput, Error, Token, TypePath,
6 parse::{self, Parse, ParseStream},
7 parse_macro_input,
8 punctuated::Punctuated,
9};
10
11struct TraitCastTargets {
13 targets: Vec<TypePath>,
14}
15
16impl Parse for TraitCastTargets {
17 fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
18 let targets: Vec<TypePath> = Punctuated::<TypePath, Token![,]>::parse_terminated(input)?
19 .into_iter()
20 .collect();
21 Ok(Self { targets })
22 }
23}
24
25impl quote::ToTokens for TraitCastTargets {
26 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
27 let vars = &self.targets;
28 tokens.extend(quote!(#(#vars),*));
29 }
30}
31
32pub fn derive_traitcastable_any(input: TokenStream) -> TokenStream {
33 let crate_path = CargoManifest::shared().resolve_crate_path("trait-cast", &[]);
34
35 let derive_input = parse_macro_input!(input as DeriveInput);
36 let source_ident = &derive_input.ident;
37
38 let traitcast_targets_attr = derive_input
39 .attrs
40 .iter()
41 .find(|attr| attr.path().is_ident("traitcast_targets"));
42
43 let trait_cast_targets = if let Some(attr) = traitcast_targets_attr {
44 match attr.parse_args::<TraitCastTargets>() {
45 Ok(targets) => targets,
46 Err(err) => {
47 return Error::new_spanned(
48 attr,
49 format!("Failed to parse traitcast_targets attribute: {err}"),
50 )
51 .to_compile_error()
52 .into();
53 },
54 }
55 } else {
56 return Error::new_spanned(
57 derive_input.ident,
58 "Missing required attribute 'traitcast_targets', e.g. #[traitcast_targets(TargetTrait1, TargetTrait2)]",
59 )
60 .to_compile_error()
61 .into();
62 };
63
64 TokenStream::from(quote!(
65 #crate_path::make_trait_castable_decl! {
66 #source_ident => (#trait_cast_targets)
67 }))
68}