synthez_core/codegen/
to_tokens.rs1use proc_macro2::{Span, TokenStream};
4use quote::quote;
5use syn::{
6 parse::{Parse, ParseStream},
7 token,
8};
9
10use crate::{
11 parse::{
12 attrs::{dedup, field::TryMerge as _, kind},
13 err,
14 ext::ParseBuffer as _,
15 },
16 ParseAttrs,
17};
18
19const TRAIT_NAME: &str = "ToTokens";
21
22const ATTR_NAME: &str = "to_tokens";
24
25pub fn derive(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
32 if !matches!(&input.data, syn::Data::Enum(_) | syn::Data::Struct(_)) {
33 return Err(syn::Error::new_spanned(
34 input,
35 format!("only structs and enums can derive {TRAIT_NAME}"),
36 ));
37 }
38
39 let attrs = Attrs::parse_attrs(ATTR_NAME, input)?;
40
41 let ty = &input.ident;
42 let (impl_generics, ty_generics, where_clause) =
43 input.generics.split_for_impl();
44
45 let impls = attrs.append.iter().map(|method| {
46 quote! {
47 ::synthez::quote::ToTokens::to_tokens(&self.#method(), out);
48 }
49 });
50
51 Ok(quote! {
52 #[automatically_derived]
53 impl #impl_generics ::synthez::quote::ToTokens for #ty #ty_generics
54 #where_clause
55 {
56 fn to_tokens(
57 &self,
58 out: &mut ::synthez::proc_macro2::TokenStream,
59 ) {
60 #( #impls )*
61 }
62 }
63 })
64}
65
66#[derive(Debug, Default)]
69struct Attrs {
70 append: Vec<syn::Ident>,
75}
76
77impl Parse for Attrs {
78 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
79 let mut out = Self::default();
80 while !input.is_empty() {
81 let ident = input.fork().parse_any_ident()?;
82 match ident.to_string().as_str() {
83 "append" => {
84 input.skip_any_ident()?;
85 for v in input.parse_eq_or_wrapped_and_punctuated::<
86 syn::Ident, token::Paren, token::Comma,
87 >()? {
88 out.append.try_merge::<kind::Value, dedup::Unique>(v)?;
89 }
90 }
91 name => {
92 return Err(err::unknown_attr_arg(&ident, name));
93 }
94 }
95 if input.try_parse::<token::Comma>()?.is_none() && !input.is_empty()
96 {
97 return Err(err::expected_followed_by_comma(&ident));
98 }
99 }
100 Ok(out)
101 }
102}
103
104impl ParseAttrs for Attrs {
105 fn try_merge(mut self, another: Self) -> syn::Result<Self> {
106 self.append
107 .try_merge_self::<kind::Value, dedup::Unique>(another.append)?;
108 Ok(self)
109 }
110
111 fn validate(&self, attr_name: &str, item_span: Span) -> syn::Result<()> {
112 if self.append.is_empty() {
113 return Err(syn::Error::new(
114 item_span,
115 format!(
116 "`#[{attr_name}(append(<function>))]` attribute is \
117 expected",
118 ),
119 ));
120 }
121 Ok(())
122 }
123}