1use proc_macro::TokenStream;
2use quote::{format_ident, quote};
3use syn::{Data, DeriveInput, Fields, Type, parse_macro_input};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6enum WrapperKind {
7 None,
8 Result,
10 Option,
11 Box,
12 Rc,
13 Arc,
14 Vec,
15 HashMap,
16 BTreeMap,
17 HashSet,
18 BTreeSet,
19 VecDeque,
20 LinkedList,
21 BinaryHeap,
22 Mutex,
24 RwLock,
25 Weak,
27 OptionBox,
33 OptionRc,
34 OptionArc,
35 BoxOption,
36 RcOption,
37 ArcOption,
38 VecOption,
39 OptionVec,
40 HashMapOption,
41 OptionHashMap,
42 ArcMutex,
44 ArcRwLock,
45 Tagged,
47}
48
49struct SomeStruct {
50 abc: String,
51}
52
53#[proc_macro_derive(Keypath)]
54pub fn derive_keypaths(input: TokenStream) -> TokenStream {
55 let ast = parse_macro_input!(input as DeriveInput);
56
57 let name = &ast.ident;
59
60 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
62
63 let methods = match &ast.data {
65 Data::Struct(data) => {
66 match &data.fields {
67 Fields::Named(fields) => {
68 let mut tokens = proc_macro2::TokenStream::new();
69 for field in &fields.named {
70 if let Some(field_name) = &field.ident {
71 let field_type = &field.ty;
72 let (kind, inner_ty) = extract_wrapper_inner_type(field_type);
73 match (kind, inner_ty.clone()) {
74 (WrapperKind::None, None) => {
76 tokens.extend(quote! {
77 pub fn #field_name() -> key_paths_core::KeyPaths<#name, #field_type> {
78 key_paths_core::KeyPaths::readable(|s: &#name| &s.#field_name)
79 }
80 });
81 }
82 (WrapperKind::Option, Some(inner_ty)) => {
84 tokens.extend(quote! {
85 pub fn #field_name() -> key_paths_core::KeyPaths<#name, #inner_ty> {
86 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_name.as_ref())
87 }
88 });
89 }
90 (WrapperKind::Result, Some(inner_ty)) => {
91 tokens.extend(quote! {
92 pub fn #field_name() -> key_paths_core::KeyPaths<#name, #inner_ty> {
93 key_paths_core::KeyPaths::failable_readable(|s: &#name| s.#field_name.as_ref().ok())
94 }
95 });
96 }
97
98 _ => {}
99 }
100 }
101 }
102
103 tokens
104 }
105 Fields::Unnamed(fields) => {
106 let mut tokens = proc_macro2::TokenStream::new();
107 for (i, field) in fields.unnamed.iter().enumerate() {
108 let field_type = &field.ty;
109 }
111 tokens
112 }
113 Fields::Unit => {
114 let mut tokens = proc_macro2::TokenStream::new();
115 tokens
117 }
118 }
119 }
120 Data::Enum(data) => {
121 let mut tokens = proc_macro2::TokenStream::new();
122 for variant in &data.variants {
123 let variant_name = &variant.ident;
124 }
126 tokens
127 }
128 Data::Union(_) => {
129 let mut tokens = proc_macro2::TokenStream::new();
130 panic!("Unions not supported");
131 tokens
132 }
133 };
134
135 let expanded = quote! {
145 impl #name {
146 #methods
147 }
148 };
149
150 TokenStream::from(expanded)
151}
152
153fn extract_wrapper_inner_type(ty: &Type) -> (WrapperKind, Option<Type>) {
154 use syn::{GenericArgument, PathArguments};
155
156 if let Type::Path(tp) = ty {
157 if let Some(seg) = tp.path.segments.last() {
158 let ident_str = seg.ident.to_string();
159
160 if let PathArguments::AngleBracketed(ab) = &seg.arguments {
161 let args: Vec<_> = ab.args.iter().collect();
162
163 if ident_str == "HashMap" || ident_str == "BTreeMap" {
165 if let (Some(_key_arg), Some(value_arg)) = (args.get(0), args.get(1)) {
166 if let GenericArgument::Type(inner) = value_arg {
167 eprintln!("Detected {} type, extracting value type", ident_str);
168 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
170 match (ident_str.as_str(), inner_kind) {
171 ("HashMap", WrapperKind::Option) => {
172 return (WrapperKind::HashMapOption, inner_inner);
173 }
174 _ => {
175 return match ident_str.as_str() {
176 "HashMap" => (WrapperKind::HashMap, Some(inner.clone())),
177 "BTreeMap" => (WrapperKind::BTreeMap, Some(inner.clone())),
178 _ => (WrapperKind::None, None),
179 };
180 }
181 }
182 }
183 }
184 }
185 else if let Some(arg) = args.get(0) {
187 if let GenericArgument::Type(inner) = arg {
188 let (inner_kind, inner_inner) = extract_wrapper_inner_type(inner);
190
191 match (ident_str.as_str(), inner_kind) {
193 ("Option", WrapperKind::Box) => {
194 return (WrapperKind::OptionBox, inner_inner);
195 }
196 ("Option", WrapperKind::Rc) => {
197 return (WrapperKind::OptionRc, inner_inner);
198 }
199 ("Option", WrapperKind::Arc) => {
200 return (WrapperKind::OptionArc, inner_inner);
201 }
202 ("Option", WrapperKind::Vec) => {
203 return (WrapperKind::OptionVec, inner_inner);
204 }
205 ("Option", WrapperKind::HashMap) => {
206 return (WrapperKind::OptionHashMap, inner_inner);
207 }
208 ("Box", WrapperKind::Option) => {
209 return (WrapperKind::BoxOption, inner_inner);
210 }
211 ("Rc", WrapperKind::Option) => {
212 return (WrapperKind::RcOption, inner_inner);
213 }
214 ("Arc", WrapperKind::Option) => {
215 return (WrapperKind::ArcOption, inner_inner);
216 }
217 ("Vec", WrapperKind::Option) => {
218 return (WrapperKind::VecOption, inner_inner);
219 }
220 ("HashMap", WrapperKind::Option) => {
221 return (WrapperKind::HashMapOption, inner_inner);
222 }
223 ("Arc", WrapperKind::Mutex) => {
224 return (WrapperKind::ArcMutex, inner_inner);
225 }
226 ("Arc", WrapperKind::RwLock) => {
227 return (WrapperKind::ArcRwLock, inner_inner);
228 }
229 _ => {
230 return match ident_str.as_str() {
232 "Option" => (WrapperKind::Option, Some(inner.clone())),
233 "Box" => (WrapperKind::Box, Some(inner.clone())),
234 "Rc" => (WrapperKind::Rc, Some(inner.clone())),
235 "Arc" => (WrapperKind::Arc, Some(inner.clone())),
236 "Vec" => (WrapperKind::Vec, Some(inner.clone())),
237 "HashSet" => (WrapperKind::HashSet, Some(inner.clone())),
238 "BTreeSet" => (WrapperKind::BTreeSet, Some(inner.clone())),
239 "VecDeque" => (WrapperKind::VecDeque, Some(inner.clone())),
240 "LinkedList" => (WrapperKind::LinkedList, Some(inner.clone())),
241 "BinaryHeap" => (WrapperKind::BinaryHeap, Some(inner.clone())),
242 "Result" => (WrapperKind::Result, Some(inner.clone())),
243 "Mutex" => (WrapperKind::Mutex, Some(inner.clone())),
244 "RwLock" => (WrapperKind::RwLock, Some(inner.clone())),
245 "Weak" => (WrapperKind::Weak, Some(inner.clone())),
246 "Tagged" => (WrapperKind::Tagged, Some(inner.clone())),
247 _ => (WrapperKind::None, None),
248 };
249 }
250 }
251 }
252 }
253 }
254 }
255 }
256 (WrapperKind::None, None)
257}
258
259fn to_snake_case(name: &str) -> String {
260 let mut out = String::new();
261 for (i, c) in name.chars().enumerate() {
262 if c.is_uppercase() {
263 if i != 0 {
264 out.push('_');
265 }
266 out.push(c.to_ascii_lowercase());
267 } else {
268 out.push(c);
269 }
270 }
271 out
272}
273
274