utility_macros_internals/derive/
record.rs

1use convert_case::{Case, Casing as _};
2use proc_macro2::{Span, TokenStream, TokenTree};
3use quote::quote;
4use syn::{Data, DeriveInput, Ident, Meta};
5
6pub fn record_impl(
7    DeriveInput {
8        attrs: type_attrs,
9        ident: type_ident,
10        data,
11        ..
12    }: DeriveInput,
13) -> TokenStream {
14    let Data::Enum(data) = data else {
15        panic!("Expected Enum")
16    };
17
18    let mut impls = Vec::new();
19
20    for attr in &type_attrs {
21        let Meta::List(meta) = &attr.meta else {
22            continue;
23        };
24
25        if meta
26            .path
27            .segments
28            .first()
29            .map_or(true, |s| s.ident != "record")
30        {
31            continue;
32        }
33
34        let mut tokens = meta.tokens.clone().into_iter();
35
36        let Some(TokenTree::Ident(ident)) = tokens.next() else {
37            panic!("Expected ident");
38        };
39
40        match tokens.next() {
41            Some(TokenTree::Punct(punct)) if punct.as_char() == '=' => {}
42            _ => {
43                panic!("Expected \"=>\"");
44            }
45        }
46
47        match tokens.next() {
48            Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => {}
49            _ => {
50                panic!("Expected \"=>\"");
51            }
52        }
53
54        let ty = tokens.next().expect("Expected type");
55        match &ty {
56            TokenTree::Literal(_) | TokenTree::Punct(_) => panic!("Expected ident or group"),
57            _ => {}
58        }
59
60        let variants = data.variants.clone().into_iter().collect::<Vec<_>>();
61
62        let idents = variants
63            .clone()
64            .into_iter()
65            .map(|v| {
66                Ident::new(
67                    v.ident.to_string().to_case(Case::Snake).as_str(),
68                    Span::call_site(),
69                )
70            })
71            .collect::<Vec<_>>();
72
73        impls.push(quote! {
74            utility_macros::_um::_sa::assert_impl_all! (#ty: Sized);
75
76            pub struct #ident {
77                #(pub #idents: #ty),*
78            }
79
80            impl utility_macros::_um::record::HasRecord for #type_ident {
81                type Record = #ident;
82            }
83
84            impl utility_macros::_um::record::Record for #ident {
85                type Keys = #type_ident;
86                type Type = #ty;
87
88                fn keys(&self) -> Vec<Self::Keys> {
89                    vec![
90                        #(#type_ident::#variants),*
91                    ]
92                }
93
94                fn values(&self) -> Vec<&Self::Type> {
95                    vec![
96                        #(&self.#idents),*
97                    ]
98                }
99                fn values_mut(&mut self) -> Vec<&mut Self::Type> {
100                    vec![
101                        #(&mut self.#idents),*
102                    ]
103                }
104
105                fn entries(&self) -> Vec<(Self::Keys, &Self::Type)> {
106                    vec![
107                        #((#type_ident::#variants, &self.#idents)),*
108                    ]
109                }
110                fn entires_mut(&mut self) -> Vec<(Self::Keys, &mut Self::Type)> {
111                    vec![
112                        #((#type_ident::#variants, &mut self.#idents)),*
113                    ]
114                }
115
116                fn try_from_entries(entries: Vec<(Self::Keys, Self::Type)>) -> utility_macros::_um::error::Result<Self> {
117                    #(let mut #idents = Option::<Self::Type>::None;)*
118
119                    for (k, v) in entries {
120                        match k {
121                            #(
122                                #type_ident::#variants => {
123                                    if let Some(_) = #idents {
124                                        return Err(utility_macros::_um::error::Error::DuplicateKey(
125                                            concat!(stringify!(#type_ident), "::", stringify!(#variants))
126                                        ));
127                                    }
128                                    #idents = Some(v);
129                                }
130                            ),*
131                        }
132                    } 
133
134                    Ok(Self {
135                        #(
136                            #idents: #idents.ok_or(utility_macros::_um::error::Error::MissingKey(
137                                concat!(stringify!(#type_ident), "::", stringify!(#variants))
138                            ))?
139                        ),*
140                    })
141                }
142            }
143
144            impl std::ops::Index<#type_ident> for #ident {
145                type Output = #ty;
146
147                fn index(&self, index: #type_ident) -> &Self::Output {
148                    match index {
149                        #(#type_ident::#variants => &self.#idents),*
150                    }
151                }
152            }
153
154            impl std::ops::IndexMut<#type_ident> for #ident {
155                fn index_mut(&mut self, index: #type_ident) -> &mut Self::Output {
156                    match index {
157                        #(#type_ident::#variants => &mut self.#idents),*
158                    }
159                }
160
161            }
162        });
163    }
164
165    quote! {
166        #(#impls)*
167    }
168}