1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{Data, DataStruct, DeriveInput, Fields};
4
5#[proc_macro_derive(Keyable)]
7pub fn derive_keyable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
8 let input = syn::parse_macro_input!(input as DeriveInput);
9 let Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) = input.data else {
10 return quote! { compile_error!("The Keyable macro can only be used on structs with named fields") }.into()
11 };
12
13 let fields_with_strs = fields.named
14 .into_iter()
15 .map(|f| (f.ident.as_ref().unwrap().to_string(), f))
18 .collect::<Vec<_>>();
19
20 let impl_consts: TokenStream = fields_with_strs.iter()
21 .map(|(s, f)| {
22 let f_ident = &f.ident;
23 quote! {
24 const #f_ident: ::kathy::KeyPath<#s> = ::kathy::KeyPath;
25 }
26 }).collect();
27
28 let input_name = input.ident;
29
30 let impl_idx: TokenStream = fields_with_strs.iter()
31 .map(|(s, f)| {
32 let f_ident = &f.ident;
33 let f_ty = &f.ty;
34
35 quote!{
36 impl<'a> ::kathy::KeyPathIndexable<::kathy::KeyPath<#s>> for &'a #input_name {
37 type Output = &'a #f_ty;
38 fn idx(self) -> Self::Output {
39 &self.#f_ident
40 }
41 }
42
43 impl<'a> ::kathy::KeyPathIndexable<::kathy::KeyPath<#s>> for &'a mut #input_name {
44 type Output = &'a mut #f_ty;
45 fn idx(self) -> Self::Output {
46 &mut self.#f_ident
47 }
48 }
49
50 impl ::kathy::KeyPathIndexable<::kathy::KeyPath<#s>> for #input_name {
51 type Output = #f_ty;
52 fn idx(self) -> Self::Output {
53 self.#f_ident
54 }
55 }
56 }
57 }).collect();
58
59
60 quote!{
61 #[allow(non_upper_case_globals)]
62 impl #input_name {
63 #impl_consts
64 }
65
66 #impl_idx
67
68 impl<T> ::core::ops::Index<T> for #input_name
69 where
70 for<'a> &'a #input_name: ::kathy::KeyPathIndexable<T>,
71 #input_name: ::kathy::KeyPathIndexable<T>,
72 for<'a> <&'a #input_name as ::kathy::KeyPathIndexable<T>>::Output:
73 ::kathy::TypeEquals<&'a <#input_name as ::kathy::KeyPathIndexable<T>>::Output>,
74 {
75 type Output = <#input_name as ::kathy::KeyPathIndexable<T>>::Output;
76 fn index(&self, _: T) -> &Self::Output {
77 use ::kathy::TypeEquals as _;
78 <&Self as ::kathy::KeyPathIndexable<T>>::idx(self)
79 .to_type()
80 }
81 }
82
83 impl<T> ::core::ops::IndexMut<T> for #input_name
84 where
85 for<'a> &'a #input_name: ::kathy::KeyPathIndexable<T>,
87 #input_name: ::kathy::KeyPathIndexable<T>,
88 for<'a> <&'a #input_name as ::kathy::KeyPathIndexable<T>>::Output:
89 ::kathy::TypeEquals<&'a <#input_name as ::kathy::KeyPathIndexable<T>>::Output>,
90 for<'a> &'a mut #input_name: ::kathy::KeyPathIndexable<T>,
92 #input_name: ::kathy::KeyPathIndexable<T>,
93 for<'a> <&'a mut #input_name as ::kathy::KeyPathIndexable<T>>::Output:
94 ::kathy::TypeEquals<&'a mut <#input_name as ::kathy::KeyPathIndexable<T>>::Output>,
95 {
96 fn index_mut(&mut self, _: T) -> &mut Self::Output {
97 use ::kathy::TypeEquals as _;
98 <&mut Self as ::kathy::KeyPathIndexable<T>>::idx(self)
99 .to_type()
100 }
101 }
102 }.into()
103}