1use proc_macro::TokenStream;
38use quote::{format_ident, quote};
39use syn::{parse_macro_input, Attribute, Data, DeriveInput, Fields, Meta};
40
41fn get_crate_path(attrs: &[Attribute]) -> proc_macro2::TokenStream {
44 for attr in attrs {
45 if attr.path().is_ident("graph") {
46 if let Meta::List(list) = &attr.meta {
47 let tokens = list.tokens.to_string();
48 if let Some(rest) = tokens.strip_prefix("crate") {
50 let rest = rest.trim();
51 if let Some(rest) = rest.strip_prefix('=') {
52 let rest = rest.trim();
53 if rest.starts_with('"') && rest.ends_with('"') {
54 let path_str = &rest[1..rest.len() - 1];
55 let path: syn::Path =
57 syn::parse_str(path_str).expect("Invalid crate path");
58 return quote! { #path };
59 }
60 }
61 }
62 }
63 }
64 }
65 quote! { packr_abi }
67}
68
69#[proc_macro_derive(GraphValue, attributes(graph))]
102pub fn derive_graph_value(input: TokenStream) -> TokenStream {
103 let input = parse_macro_input!(input as DeriveInput);
104 let crate_path = get_crate_path(&input.attrs);
105
106 let expanded = match &input.data {
107 Data::Struct(data) => derive_struct(&input, data, &crate_path),
108 Data::Enum(data) => derive_enum(&input, data, &crate_path),
109 Data::Union(_) => {
110 return syn::Error::new_spanned(&input, "GraphValue cannot be derived for unions")
111 .to_compile_error()
112 .into();
113 }
114 };
115
116 expanded.into()
117}
118
119fn derive_struct(
120 input: &DeriveInput,
121 data: &syn::DataStruct,
122 krate: &proc_macro2::TokenStream,
123) -> proc_macro2::TokenStream {
124 let name = &input.ident;
125 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
126
127 match &data.fields {
128 Fields::Named(fields) => {
129 let field_from_value: Vec<_> = fields.named.iter().map(|f| {
131 let field_name = f.ident.as_ref().unwrap();
132 let field_name_str = get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string());
133 let field_type = &f.ty;
134 quote! {
135 #field_name: {
136 let field_value = fields.iter()
137 .find(|(name, _)| name == #field_name_str)
138 .map(|(_, v)| v.clone())
139 .ok_or_else(|| #krate::ConversionError::MissingField(
140 #krate::__private::String::from(#field_name_str)
141 ))?;
142 <#field_type as #krate::__private::TryFrom<#krate::Value>>::try_from(field_value)
143 .map_err(|e| #krate::ConversionError::FieldError(
144 #krate::__private::String::from(#field_name_str),
145 #krate::__private::Box::new(e)
146 ))?
147 }
148 }
149 }).collect();
150
151 let field_count = fields.named.len();
152
153 let field_accessors: Vec<_> = fields
155 .named
156 .iter()
157 .map(|f| {
158 let field_name = f.ident.as_ref().unwrap();
159 let field_name_str =
160 get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string());
161 quote! {
162 (
163 #krate::__private::String::from(#field_name_str),
164 #krate::Value::from(value.#field_name)
165 )
166 }
167 })
168 .collect();
169
170 let type_name_str = name.to_string();
171
172 quote! {
173 impl #impl_generics #krate::__private::From<#name #ty_generics> for #krate::Value #where_clause {
174 fn from(value: #name #ty_generics) -> #krate::Value {
175 #krate::Value::Record {
176 type_name: #krate::__private::String::from(#type_name_str),
177 fields: #krate::__private::vec![
178 #(#field_accessors),*
179 ],
180 }
181 }
182 }
183
184 impl #impl_generics #krate::__private::TryFrom<#krate::Value> for #name #ty_generics #where_clause {
185 type Error = #krate::ConversionError;
186
187 fn try_from(value: #krate::Value) -> #krate::__private::Result<Self, Self::Error> {
188 match value {
189 #krate::Value::Record { fields, .. } => {
190 if fields.len() != #field_count {
191 return #krate::__private::Err(#krate::ConversionError::WrongFieldCount {
192 expected: #field_count,
193 got: fields.len(),
194 });
195 }
196 #krate::__private::Ok(Self {
197 #(#field_from_value),*
198 })
199 }
200 other => #krate::__private::Err(#krate::ConversionError::ExpectedRecord(
201 #krate::__private::format!("{:?}", other)
202 )),
203 }
204 }
205 }
206
207 impl #impl_generics #krate::KnownValueType for #name #ty_generics #where_clause {
208 fn known_value_type() -> #krate::ValueType {
209 #krate::ValueType::Record(
210 #krate::__private::String::from(#type_name_str)
211 )
212 }
213 }
214 }
215 }
216 Fields::Unnamed(fields) => {
217 let field_indices: Vec<_> = (0..fields.unnamed.len()).map(syn::Index::from).collect();
219
220 let field_from_value: Vec<_> = fields.unnamed.iter().enumerate().map(|(i, f)| {
221 let field_type = &f.ty;
222 quote! {
223 <#field_type as #krate::__private::TryFrom<#krate::Value>>::try_from(
224 fields.get(#i).cloned().ok_or_else(|| #krate::ConversionError::MissingIndex(#i))?
225 ).map_err(|e| #krate::ConversionError::IndexError(#i, #krate::__private::Box::new(e)))?
226 }
227 }).collect();
228
229 let field_count = fields.unnamed.len();
230 let field_types: Vec<_> = fields.unnamed.iter().map(|f| &f.ty).collect();
231
232 quote! {
233 impl #impl_generics #krate::__private::From<#name #ty_generics> for #krate::Value #where_clause {
234 fn from(value: #name #ty_generics) -> #krate::Value {
235 #krate::Value::Tuple(#krate::__private::vec![
236 #(#krate::Value::from(value.#field_indices)),*
237 ])
238 }
239 }
240
241 impl #impl_generics #krate::__private::TryFrom<#krate::Value> for #name #ty_generics #where_clause {
242 type Error = #krate::ConversionError;
243
244 fn try_from(value: #krate::Value) -> #krate::__private::Result<Self, Self::Error> {
245 match value {
246 #krate::Value::Tuple(fields) => {
247 if fields.len() != #field_count {
248 return #krate::__private::Err(#krate::ConversionError::WrongFieldCount {
249 expected: #field_count,
250 got: fields.len(),
251 });
252 }
253 #krate::__private::Ok(Self(
254 #(#field_from_value),*
255 ))
256 }
257 other => #krate::__private::Err(#krate::ConversionError::ExpectedTuple(
258 #krate::__private::format!("{:?}", other)
259 )),
260 }
261 }
262 }
263
264 impl #impl_generics #krate::KnownValueType for #name #ty_generics #where_clause {
265 fn known_value_type() -> #krate::ValueType {
266 #krate::ValueType::Tuple(#krate::__private::vec![
267 #(<#field_types as #krate::KnownValueType>::known_value_type()),*
268 ])
269 }
270 }
271 }
272 }
273 Fields::Unit => {
274 quote! {
276 impl #impl_generics #krate::__private::From<#name #ty_generics> for #krate::Value #where_clause {
277 fn from(_: #name #ty_generics) -> #krate::Value {
278 #krate::Value::Tuple(#krate::__private::vec![])
279 }
280 }
281
282 impl #impl_generics #krate::__private::TryFrom<#krate::Value> for #name #ty_generics #where_clause {
283 type Error = #krate::ConversionError;
284
285 fn try_from(value: #krate::Value) -> #krate::__private::Result<Self, Self::Error> {
286 match value {
287 #krate::Value::Tuple(fields) if fields.is_empty() => {
288 #krate::__private::Ok(Self)
289 }
290 #krate::Value::Tuple(fields) => {
291 #krate::__private::Err(#krate::ConversionError::WrongFieldCount {
292 expected: 0,
293 got: fields.len(),
294 })
295 }
296 other => #krate::__private::Err(#krate::ConversionError::ExpectedTuple(
297 #krate::__private::format!("{:?}", other)
298 )),
299 }
300 }
301 }
302
303 impl #impl_generics #krate::KnownValueType for #name #ty_generics #where_clause {
304 fn known_value_type() -> #krate::ValueType {
305 #krate::ValueType::Tuple(#krate::__private::vec![])
306 }
307 }
308 }
309 }
310 }
311}
312
313fn derive_enum(
314 input: &DeriveInput,
315 data: &syn::DataEnum,
316 krate: &proc_macro2::TokenStream,
317) -> proc_macro2::TokenStream {
318 let name = &input.ident;
319 let type_name_str = name.to_string();
320 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
321
322 let to_value_arms: Vec<_> = data
324 .variants
325 .iter()
326 .enumerate()
327 .map(|(default_tag, variant)| {
328 let variant_name = &variant.ident;
329 let case_name_str = variant_name.to_string();
330 let tag = get_tag(&variant.attrs).unwrap_or(default_tag);
331
332 match &variant.fields {
333 Fields::Named(fields) => {
334 let field_names: Vec<_> = fields
335 .named
336 .iter()
337 .map(|f| f.ident.as_ref().unwrap())
338 .collect();
339 let field_to_value: Vec<_> = fields
341 .named
342 .iter()
343 .map(|f| {
344 let field_name = f.ident.as_ref().unwrap();
345 let field_name_str =
346 get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string());
347 quote! {
348 (
349 #krate::__private::String::from(#field_name_str),
350 #krate::Value::from(#field_name)
351 )
352 }
353 })
354 .collect();
355
356 quote! {
357 #name::#variant_name { #(#field_names),* } => {
358 #krate::Value::Variant {
359 type_name: #krate::__private::String::from(#type_name_str),
360 case_name: #krate::__private::String::from(#case_name_str),
361 tag: #tag,
362 payload: #krate::__private::vec![
363 #krate::Value::Record {
364 type_name: #krate::__private::String::from(#case_name_str),
365 fields: #krate::__private::vec![#(#field_to_value),*],
366 }
367 ],
368 }
369 }
370 }
371 }
372 Fields::Unnamed(fields) => {
373 let field_names: Vec<_> = (0..fields.unnamed.len())
374 .map(|i| format_ident!("f{}", i))
375 .collect();
376
377 quote! {
379 #name::#variant_name(#(#field_names),*) => {
380 #krate::Value::Variant {
381 type_name: #krate::__private::String::from(#type_name_str),
382 case_name: #krate::__private::String::from(#case_name_str),
383 tag: #tag,
384 payload: #krate::__private::vec![
385 #(#krate::Value::from(#field_names)),*
386 ],
387 }
388 }
389 }
390 }
391 Fields::Unit => {
392 quote! {
393 #name::#variant_name => {
394 #krate::Value::Variant {
395 type_name: #krate::__private::String::from(#type_name_str),
396 case_name: #krate::__private::String::from(#case_name_str),
397 tag: #tag,
398 payload: #krate::__private::vec![],
399 }
400 }
401 }
402 }
403 }
404 })
405 .collect();
406
407 let from_value_arms: Vec<_> = data.variants.iter().enumerate().map(|(default_tag, variant)| {
409 let variant_name = &variant.ident;
410 let tag = get_tag(&variant.attrs).unwrap_or(default_tag);
411
412 match &variant.fields {
413 Fields::Named(fields) => {
414 let field_from_value: Vec<_> = fields.named.iter().map(|f| {
415 let field_name = f.ident.as_ref().unwrap();
416 let field_name_str = get_rename(&f.attrs).unwrap_or_else(|| field_name.to_string());
417 let field_type = &f.ty;
418 quote! {
419 #field_name: {
420 let field_value = record_fields.iter()
421 .find(|(name, _)| name == #field_name_str)
422 .map(|(_, v)| v.clone())
423 .ok_or_else(|| #krate::ConversionError::MissingField(
424 #krate::__private::String::from(#field_name_str)
425 ))?;
426 <#field_type as #krate::__private::TryFrom<#krate::Value>>::try_from(field_value)
427 .map_err(|e| #krate::ConversionError::FieldError(
428 #krate::__private::String::from(#field_name_str),
429 #krate::__private::Box::new(e)
430 ))?
431 }
432 }
433 }).collect();
434
435 quote! {
436 #tag => {
437 if payload.len() != 1 {
439 return #krate::__private::Err(#krate::ConversionError::WrongFieldCount {
440 expected: 1,
441 got: payload.len(),
442 });
443 }
444 match &payload[0] {
445 #krate::Value::Record { fields: record_fields, .. } => {
446 #krate::__private::Ok(#name::#variant_name {
447 #(#field_from_value),*
448 })
449 }
450 other => #krate::__private::Err(#krate::ConversionError::ExpectedRecord(
451 #krate::__private::format!("{:?}", other)
452 )),
453 }
454 }
455 }
456 }
457 Fields::Unnamed(fields) => {
458 let field_count = fields.unnamed.len();
459 let field_conversions: Vec<_> = fields.unnamed.iter().enumerate().map(|(i, f)| {
460 let field_type = &f.ty;
461 quote! {
462 <#field_type as #krate::__private::TryFrom<#krate::Value>>::try_from(
463 payload.get(#i).cloned().ok_or_else(|| #krate::ConversionError::MissingIndex(#i))?
464 ).map_err(|e| #krate::ConversionError::IndexError(#i, #krate::__private::Box::new(e)))?
465 }
466 }).collect();
467
468 quote! {
469 #tag => {
470 if payload.len() != #field_count {
471 return #krate::__private::Err(#krate::ConversionError::WrongFieldCount {
472 expected: #field_count,
473 got: payload.len(),
474 });
475 }
476 #krate::__private::Ok(#name::#variant_name(
477 #(#field_conversions),*
478 ))
479 }
480 }
481 }
482 Fields::Unit => {
483 quote! {
484 #tag => {
485 if !payload.is_empty() {
486 return #krate::__private::Err(#krate::ConversionError::UnexpectedPayload);
487 }
488 #krate::__private::Ok(#name::#variant_name)
489 }
490 }
491 }
492 }
493 }).collect();
494
495 let variant_count = data.variants.len();
496
497 quote! {
498 impl #impl_generics #krate::__private::From<#name #ty_generics> for #krate::Value #where_clause {
499 fn from(value: #name #ty_generics) -> #krate::Value {
500 match value {
501 #(#to_value_arms),*
502 }
503 }
504 }
505
506 impl #impl_generics #krate::__private::TryFrom<#krate::Value> for #name #ty_generics #where_clause {
507 type Error = #krate::ConversionError;
508
509 fn try_from(value: #krate::Value) -> #krate::__private::Result<Self, Self::Error> {
510 match value {
511 #krate::Value::Variant { tag, payload, .. } => {
512 match tag {
513 #(#from_value_arms),*
514 other => #krate::__private::Err(#krate::ConversionError::UnknownTag {
515 tag: other,
516 max: #variant_count,
517 }),
518 }
519 }
520 other => #krate::__private::Err(#krate::ConversionError::ExpectedVariant(
521 #krate::__private::format!("{:?}", other)
522 )),
523 }
524 }
525 }
526
527 impl #impl_generics #krate::KnownValueType for #name #ty_generics #where_clause {
528 fn known_value_type() -> #krate::ValueType {
529 #krate::ValueType::Variant(#krate::__private::String::from(#type_name_str))
530 }
531 }
532 }
533}
534
535fn get_rename(attrs: &[Attribute]) -> Option<String> {
537 for attr in attrs {
538 if attr.path().is_ident("graph") {
539 if let Meta::List(list) = &attr.meta {
540 let tokens = list.tokens.to_string();
541 if let Some(rest) = tokens.strip_prefix("rename") {
543 let rest = rest.trim();
544 if let Some(rest) = rest.strip_prefix('=') {
545 let rest = rest.trim();
546 if rest.starts_with('"') && rest.ends_with('"') {
547 return Some(rest[1..rest.len() - 1].to_string());
548 }
549 }
550 }
551 }
552 }
553 }
554 None
555}
556
557fn get_tag(attrs: &[Attribute]) -> Option<usize> {
559 for attr in attrs {
560 if attr.path().is_ident("graph") {
561 if let Meta::List(list) = &attr.meta {
562 let tokens = list.tokens.to_string();
563 if let Some(rest) = tokens.strip_prefix("tag") {
565 let rest = rest.trim();
566 if let Some(rest) = rest.strip_prefix('=') {
567 let rest = rest.trim();
568 return rest.parse().ok();
569 }
570 }
571 }
572 }
573 }
574 None
575}