1use proc_macro::TokenStream;
2use quote::{quote, ToTokens};
3use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, GenericParam, Type};
4struct TucoType {
5 original: Type,
6 tuple: Type,
7}
8
9enum FieldTypeInfo {
10 Tuco(TucoType),
11 Standard(Type),
12}
13
14struct StructFieldEntry {
15 single: bool,
16 index: usize,
17 ident: Option<syn::Ident>,
18 t: FieldTypeInfo,
19}
20
21impl StructFieldEntry {
22 fn get_original_type(&self) -> proc_macro2::TokenStream {
23 match &self.t {
24 FieldTypeInfo::Tuco(tuco) => tuco.original.to_token_stream(),
25 FieldTypeInfo::Standard(x) => x.to_token_stream(),
26 }
27 }
28
29 fn get_tuple_type(&self) -> proc_macro2::TokenStream {
30 match &self.t {
31 FieldTypeInfo::Tuco(tuco) => tuco.tuple.to_token_stream(),
32 FieldTypeInfo::Standard(x) => x.to_token_stream(),
33 }
34 }
35
36 fn get_convert_to_tuple_row(&self) -> proc_macro2::TokenStream {
37 match &self.ident {
38 Some(ident) => match &self.t {
39 FieldTypeInfo::Tuco(_) => {
40 quote! { self.#ident.into_tuple() }
41 }
42 FieldTypeInfo::Standard(_) => {
43 quote! { self.#ident }
44 }
45 },
46 None => {
47 let index = syn::Index::from(self.index);
48 match &self.t {
49 FieldTypeInfo::Tuco(_) => {
50 quote! { self.#index.into_tuple() }
51 }
52 FieldTypeInfo::Standard(_) => {
53 quote! { self.#index }
54 }
55 }
56 }
57 }
58 }
59
60 fn get_convert_from_tuple_row(&self) -> proc_macro2::TokenStream {
61 let index = syn::Index::from(self.index);
62 let type_token = self.get_original_type();
63 let input_select = if self.single {
64 quote! { tuple }
65 } else {
66 quote! { tuple.#index }
67 };
68
69 match &self.ident {
70 Some(ident) => match &self.t {
71 FieldTypeInfo::Tuco(_) => {
72 quote! { #ident : <#type_token as Tuco>::from_tuple(#input_select) }
73 }
74 FieldTypeInfo::Standard(_) => {
75 quote! { #ident : #input_select }
76 }
77 },
78 None => match &self.t {
79 FieldTypeInfo::Tuco(_) => {
80 quote! { <#type_token as Tuco>::from_tuple(#input_select) }
81 }
82 FieldTypeInfo::Standard(_) => {
83 quote! { #input_select }
84 }
85 },
86 }
87 }
88}
89
90fn has_tuco_meta_tag(field: &Field) -> bool {
91 field.attrs.iter().any(|x| x.path().is_ident("tuco"))
92}
93
94fn parse_struct_field(
95 index: usize,
96 field: &Field,
97 single: bool,
98) -> Result<StructFieldEntry, String> {
99 if has_tuco_meta_tag(field) {
100 let type_as_string = field.ty.to_token_stream().to_string().replace(" ", "");
101 let tuple_type = format!("<{} as Tuco>::Tuple", type_as_string);
102 let parsed_type: syn::Type = match syn::parse_str(&tuple_type) {
103 Ok(t) => t,
104 _ => return Err("Failed to parse type".to_string()),
105 };
106
107 Ok(StructFieldEntry {
108 single,
109 index,
110 ident: field.ident.clone(),
111 t: FieldTypeInfo::Tuco(TucoType {
112 original: field.ty.clone(),
113 tuple: parsed_type,
114 }),
115 })
116 } else {
117 Ok(StructFieldEntry {
118 single,
119 index,
120 ident: field.ident.clone(),
121 t: FieldTypeInfo::Standard(field.ty.clone()),
122 })
123 }
124}
125
126#[proc_macro_attribute]
127pub fn tuco(_: TokenStream, _: TokenStream) -> TokenStream {
128 TokenStream::new()
129}
130
131#[proc_macro_derive(Tuco, attributes(tuco))]
132pub fn derive_tuco(input: TokenStream) -> TokenStream {
133 let input = parse_macro_input!(input as DeriveInput);
134
135 let struct_name = input.ident;
137
138 let generics = input.generics;
140 let where_clause: &Option<syn::WhereClause> = &generics.where_clause;
141 let generic_params: Vec<_> = generics
142 .params
143 .iter()
144 .map(|param| match param {
145 GenericParam::Type(type_param) => quote! { #type_param },
146 GenericParam::Lifetime(lifetime) => quote! { #lifetime },
147 GenericParam::Const(const_param) => quote! { #const_param },
148 })
149 .collect();
150
151 let impl_statement = quote! { impl<#(#generic_params),*> Tuco for #struct_name<#(#generic_params),*> #where_clause };
152
153 let Data::Struct(data_struct) = &input.data else {
154 panic!("Tuco can only be used with structs.");
155 };
156 let single = data_struct.fields.len() == 1;
157 let fields: Vec<StructFieldEntry> = match &data_struct.fields {
158 Fields::Named(fields_named) => fields_named
159 .named
160 .iter()
161 .enumerate()
162 .map(|(i, f)| parse_struct_field(i, f, single).unwrap())
163 .collect(),
164 Fields::Unnamed(fields) => fields
165 .unnamed
166 .iter()
167 .enumerate()
168 .map(|(i, f)| parse_struct_field(i, f, single).unwrap())
169 .collect(),
170 Fields::Unit => Vec::new(),
171 };
172
173 let tuple_types: Vec<_> = fields
174 .iter()
175 .map(StructFieldEntry::get_tuple_type)
176 .collect();
177
178 let from_tuple_rows: Vec<_> = fields
179 .iter()
180 .map(StructFieldEntry::get_convert_from_tuple_row)
181 .collect();
182
183 let from_tuple_impl = match &data_struct.fields {
184 Fields::Named(_) => match fields.len() {
185 0 => quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name{} }},
186 1 => {
187 let first = from_tuple_rows.first().unwrap();
188 quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name{#first} }}
189 }
190 _ => {
191 quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name{#(#from_tuple_rows),*} }}
192 }
193 },
194 Fields::Unnamed(_) => match fields.len() {
195 0 => quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name() }},
196 1 => {
197 let first = from_tuple_rows.first().unwrap();
198 quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name(#first) }}
199 }
200 _ => {
201 quote! {fn from_tuple(tuple: Self::Tuple) -> Self { #struct_name(#(#from_tuple_rows),*) }}
202 }
203 },
204 Fields::Unit => {
205 quote! {fn from_tuple(tuple: Self::Tuple) -> Self {#struct_name} }
206 }
207 };
208
209 let into_tuple_rows: Vec<_> = fields
210 .iter()
211 .map(StructFieldEntry::get_convert_to_tuple_row)
212 .collect();
213
214 let into_tuple_impl = match &data_struct.fields {
215 Fields::Unnamed(_) | Fields::Named(_) => match fields.len() {
216 0 => quote! {fn into_tuple(self) -> Self::Tuple { () }},
217 1 => {
218 let first = into_tuple_rows.first().unwrap();
219 quote! {fn into_tuple(self) -> Self::Tuple { #first }}
220 }
221 _ => quote! {fn into_tuple(self) -> Self::Tuple { (#(#into_tuple_rows),*,) }},
222 },
223 Fields::Unit => {
224 quote! {fn into_tuple(self) -> Self::Tuple { () }}
225 }
226 };
227
228 let associated_type_assign = match &data_struct.fields {
229 Fields::Unnamed(_) | Fields::Named(_) => match fields.len() {
230 0 => quote! {type Tuple = ();},
231 1 => {
232 let first = tuple_types.first().unwrap();
233 quote! {type Tuple = #first;}
234 }
235 _ => quote! {type Tuple = (#(#tuple_types),*,);},
236 },
237 Fields::Unit => {
238 quote! {type Tuple = ();}
239 }
240 };
241
242 let decompose_impl = quote! {
243 #[automatically_derived]
244 #impl_statement {
245 #associated_type_assign
246
247 #into_tuple_impl
248
249 #from_tuple_impl
250
251 fn from_tuco<TFromTuco: Tuco<Tuple = Self::Tuple>>(value: TFromTuco) -> Self {
252 Self::from_tuple(value.into_tuple())
253 }
254 }
255 };
256
257 decompose_impl.into()
258}