kathy_macros/
lib.rs

1use proc_macro2::TokenStream;
2use quote::quote;
3use syn::{Data, DataStruct, DeriveInput, Fields};
4
5/// Please see the main page of `kathy`'s docs to learn how to use this.
6#[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		// we can unwrap here 'cause we already verified up above that they're named fields, not
16		// unnamed
17		.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			// need to include the requirements from Index
86			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			// and then here are the requirements specific to IndexMut
91			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}