trait_cast_macros/
derive_traitcastable_any.rs

1use 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
11/// Helper struct to parse attribute arguments like `#[traitcast_targets(Ident, Ident, ...)]`.
12struct 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}