relay_protocol_derive/
lib.rs

1//! Derives for Relay's protocol traits.
2
3#![doc(
4    html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
5    html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
6)]
7#![recursion_limit = "256"]
8
9use std::str::FromStr;
10
11use proc_macro2::{Span, TokenStream};
12use quote::{ToTokens, quote};
13use syn::spanned::Spanned;
14use syn::{Data, Error, Lit, LitStr};
15use synstructure::decl_derive;
16
17mod utils;
18
19use self::utils::SynstructureExt as _;
20
21#[derive(Debug, Clone, Copy)]
22enum Trait {
23    From,
24    To,
25}
26
27decl_derive!([Empty, attributes(metastructure)] => derive_empty);
28decl_derive!([IntoValue, attributes(metastructure)] => derive_to_value);
29decl_derive!([FromValue, attributes(metastructure)] => derive_from_value);
30
31fn derive_empty(mut s: synstructure::Structure<'_>) -> syn::Result<TokenStream> {
32    let _ = s.add_bounds(synstructure::AddBounds::Generics);
33
34    let is_empty_arms = s.try_each_variant(|variant| {
35        let mut is_tuple_struct = false;
36        let mut cond = quote!(true);
37        for (index, bi) in variant.bindings().iter().enumerate() {
38            let field_attrs = parse_field_attributes(index, bi.ast(), &mut is_tuple_struct)?;
39            let ident = &bi.binding;
40            if field_attrs.additional_properties {
41                cond = quote! {
42                    #cond && #bi.values().all(::relay_protocol::Empty::is_empty)
43                };
44            } else {
45                cond = quote! {
46                    #cond && ::relay_protocol::Empty::is_empty(#ident)
47                };
48            }
49        }
50
51        Ok(cond)
52    })?;
53
54    let is_deep_empty_arms = s.try_each_variant(|variant| {
55        if is_newtype_variant(variant) {
56            let ident = &variant.bindings()[0].binding;
57            return Ok(quote! {
58                ::relay_protocol::Empty::is_deep_empty(#ident)
59            });
60        }
61
62        let mut cond = quote!(true);
63        let mut is_tuple_struct = false;
64        for (index, bi) in variant.bindings().iter().enumerate() {
65            let field_attrs = parse_field_attributes(index, bi.ast(), &mut is_tuple_struct)?;
66            let ident = &bi.binding;
67            let skip_serialization_attr = field_attrs.skip_serialization.as_tokens();
68
69            if field_attrs.additional_properties {
70                cond = quote! {
71                    #cond && #bi.values().all(|v| v.skip_serialization(#skip_serialization_attr))
72                };
73            } else if field_attrs.flatten {
74                cond = quote! {
75                    #cond && ::relay_protocol::Empty::is_deep_empty(#ident)
76                };
77            } else {
78                cond = quote! {
79                    #cond && #ident.skip_serialization(#skip_serialization_attr)
80                };
81            }
82        }
83
84        Ok(cond)
85    })?;
86
87    Ok(s.gen_impl(quote! {
88        #[automatically_derived]
89        gen impl ::relay_protocol::Empty for @Self {
90            fn is_empty(&self) -> bool {
91                match *self {
92                    #is_empty_arms
93                }
94            }
95
96            fn is_deep_empty(&self) -> bool {
97                match *self {
98                    #is_deep_empty_arms
99                }
100            }
101        }
102    }))
103}
104
105fn derive_to_value(s: synstructure::Structure<'_>) -> syn::Result<TokenStream> {
106    derive_metastructure(s, Trait::To)
107}
108
109fn derive_from_value(s: synstructure::Structure<'_>) -> syn::Result<TokenStream> {
110    derive_metastructure(s, Trait::From)
111}
112
113fn derive_newtype_metastructure(
114    s: &synstructure::Structure<'_>,
115    t: Trait,
116) -> syn::Result<TokenStream> {
117    let type_attrs = parse_type_attributes(s)?;
118    if type_attrs.tag_key.is_some() {
119        panic!("tag_key not supported on structs");
120    }
121
122    let field_attrs = parse_field_attributes(0, s.variants()[0].bindings()[0].ast(), &mut true)?;
123    let skip_serialization_attr = field_attrs.skip_serialization.as_tokens();
124
125    let name = &s.ast().ident;
126
127    Ok(match t {
128        Trait::From => s.gen_impl(quote! {
129            #[automatically_derived]
130            gen impl ::relay_protocol::FromValue for @Self {
131                fn from_value(
132                    __value: ::relay_protocol::Annotated<::relay_protocol::Value>,
133                ) -> ::relay_protocol::Annotated<Self> {
134                    match ::relay_protocol::FromValue::from_value(__value) {
135                        Annotated(Some(__value), __meta) => Annotated(Some(#name(__value)), __meta),
136                        Annotated(None, __meta) => Annotated(None, __meta),
137                    }
138                }
139            }
140        }),
141        Trait::To => s.gen_impl(quote! {
142            #[automatically_derived]
143            gen impl ::relay_protocol::IntoValue for @Self {
144                fn into_value(self) -> ::relay_protocol::Value {
145                    ::relay_protocol::IntoValue::into_value(self.0)
146                }
147
148                fn serialize_payload<S>(&self, __serializer: S, __behavior: ::relay_protocol::SkipSerialization) -> Result<S::Ok, S::Error>
149                where
150                    Self: Sized,
151                    S: ::serde::ser::Serializer
152                {
153                    ::relay_protocol::IntoValue::serialize_payload(&self.0, __serializer, #skip_serialization_attr)
154                }
155
156                fn extract_child_meta(&self) -> ::relay_protocol::MetaMap
157                where
158                    Self: Sized,
159                {
160                    ::relay_protocol::IntoValue::extract_child_meta(&self.0)
161                }
162            }
163        }),
164    })
165}
166
167fn derive_enum_metastructure(
168    s: &synstructure::Structure<'_>,
169    t: Trait,
170) -> syn::Result<TokenStream> {
171    let type_attrs = parse_type_attributes(s)?;
172
173    let type_name = &s.ast().ident;
174    let tag_key_str = LitStr::new(
175        &type_attrs.tag_key.unwrap_or_else(|| "type".to_string()),
176        Span::call_site(),
177    );
178
179    let mut from_value_body = TokenStream::new();
180    let mut to_value_body = TokenStream::new();
181    let mut serialize_body = TokenStream::new();
182    let mut extract_child_meta_body = TokenStream::new();
183
184    for variant in s.variants() {
185        let variant_attrs = parse_variant_attributes(variant.ast().attrs)?;
186        let variant_name = &variant.ast().ident;
187        let tag = variant_attrs
188            .tag_override
189            .unwrap_or_else(|| variant_name.to_string().to_lowercase());
190
191        if !variant_attrs.fallback_variant {
192            let tag = LitStr::new(&tag, Span::call_site());
193            (quote! {
194                Some(#tag) => {
195                    ::relay_protocol::FromValue::from_value(::relay_protocol::Annotated(Some(::relay_protocol::Value::Object(__object)), __meta))
196                        .map_value(#type_name::#variant_name)
197                }
198            }).to_tokens(&mut from_value_body);
199            (quote! {
200                #type_name::#variant_name(__value) => {
201                    let mut __rv = ::relay_protocol::IntoValue::into_value(__value);
202                    if let ::relay_protocol::Value::Object(ref mut __object) = __rv {
203                        __object.insert(#tag_key_str.to_string(), Annotated::new(::relay_protocol::Value::String(#tag.to_string())));
204                    }
205                    __rv
206                }
207            }).to_tokens(&mut to_value_body);
208            (quote! {
209                #type_name::#variant_name(ref __value) => {
210                    let mut __map_ser = ::serde::Serializer::serialize_map(__serializer, None)?;
211                    ::relay_protocol::IntoValue::serialize_payload(__value, ::serde::__private::ser::FlatMapSerializer(&mut __map_ser), __behavior)?;
212                    ::serde::ser::SerializeMap::serialize_key(&mut __map_ser, #tag_key_str)?;
213                    ::serde::ser::SerializeMap::serialize_value(&mut __map_ser, #tag)?;
214                    ::serde::ser::SerializeMap::end(__map_ser)
215                }
216            }).to_tokens(&mut serialize_body);
217        } else {
218            (quote! {
219                _ => {
220                    if let Some(__type) = __type {
221                        __object.insert(#tag_key_str.to_string(), __type);
222                    }
223                    ::relay_protocol::Annotated(Some(#type_name::#variant_name(__object)), __meta)
224                }
225            })
226            .to_tokens(&mut from_value_body);
227            (quote! {
228                #type_name::#variant_name(__value) => {
229                    ::relay_protocol::IntoValue::into_value(__value)
230                }
231            })
232            .to_tokens(&mut to_value_body);
233            (quote! {
234                #type_name::#variant_name(ref __value) => {
235                    ::relay_protocol::IntoValue::serialize_payload(__value, __serializer, __behavior)
236                }
237            })
238            .to_tokens(&mut serialize_body);
239        }
240
241        (quote! {
242            #type_name::#variant_name(ref __value) => {
243                ::relay_protocol::IntoValue::extract_child_meta(__value)
244            }
245        })
246        .to_tokens(&mut extract_child_meta_body);
247    }
248
249    Ok(match t {
250        Trait::From => {
251            s.gen_impl(quote! {
252                #[automatically_derived]
253                gen impl ::relay_protocol::FromValue for @Self {
254                    fn from_value(
255                        __value: ::relay_protocol::Annotated<::relay_protocol::Value>,
256                    ) -> ::relay_protocol::Annotated<Self> {
257                        match ::relay_protocol::Object::<::relay_protocol::Value>::from_value(__value) {
258                            ::relay_protocol::Annotated(Some(mut __object), __meta) => {
259                                let __type = __object.remove(#tag_key_str);
260                                match __type.as_ref().and_then(|__type| __type.0.as_ref()).and_then(Value::as_str) {
261                                    #from_value_body
262                                }
263                            }
264                            ::relay_protocol::Annotated(None, __meta) => ::relay_protocol::Annotated(None, __meta)
265                        }
266                    }
267                }
268            })
269        }
270        Trait::To => {
271            s.gen_impl(quote! {
272                #[automatically_derived]
273                gen impl ::relay_protocol::IntoValue for @Self {
274                    fn into_value(self) -> ::relay_protocol::Value {
275                        match self {
276                            #to_value_body
277                        }
278                    }
279
280                    fn serialize_payload<S>(&self, __serializer: S, __behavior: ::relay_protocol::SkipSerialization) -> Result<S::Ok, S::Error>
281                    where
282                        S: ::serde::ser::Serializer
283                    {
284                        match *self {
285                            #serialize_body
286                        }
287                    }
288
289                    fn extract_child_meta(&self) -> ::relay_protocol::MetaMap
290                    where
291                        Self: Sized,
292                    {
293                        match *self {
294                            #extract_child_meta_body
295                        }
296                    }
297                }
298            })
299        }
300    })
301}
302
303fn derive_metastructure(mut s: synstructure::Structure<'_>, t: Trait) -> syn::Result<TokenStream> {
304    if is_newtype(&s) {
305        return derive_newtype_metastructure(&s, t);
306    }
307
308    if let Data::Enum(_) = s.ast().data {
309        return derive_enum_metastructure(&s, t);
310    }
311
312    let _ = s.add_bounds(synstructure::AddBounds::Generics);
313
314    let variants = s.variants();
315    if variants.len() != 1 {
316        panic!("Can only derive structs");
317    }
318
319    let mut variant = variants[0].clone();
320    for binding in variant.bindings_mut() {
321        binding.style = synstructure::BindStyle::MoveMut;
322    }
323
324    let mut from_value_body = TokenStream::new();
325    let mut to_value_body = TokenStream::new();
326    let mut serialize_body = TokenStream::new();
327    let mut extract_child_meta_body = TokenStream::new();
328
329    let type_attrs = parse_type_attributes(&s)?;
330    if type_attrs.tag_key.is_some() {
331        panic!("tag_key not supported on structs");
332    }
333
334    let mut is_tuple_struct = false;
335
336    for (index, bi) in variant.bindings().iter().enumerate() {
337        let field_attrs = parse_field_attributes(index, bi.ast(), &mut is_tuple_struct)?;
338        let field_name = field_attrs.field_name.clone();
339
340        let skip_serialization_attr = field_attrs.skip_serialization.as_tokens();
341
342        if field_attrs.additional_properties {
343            if is_tuple_struct {
344                panic!("additional_properties not allowed in tuple struct");
345            }
346
347            (quote! {
348                let #bi = ::std::mem::take(__obj).into_iter().map(|(__key, __value)| (__key, ::relay_protocol::FromValue::from_value(__value))).collect();
349            }).to_tokens(&mut from_value_body);
350
351            (quote! {
352                __map.extend(#bi.into_iter().map(|(__key, __value)| (
353                    __key,
354                    Annotated::map_value(__value, ::relay_protocol::IntoValue::into_value)
355                )));
356            })
357            .to_tokens(&mut to_value_body);
358
359            (quote! {
360                for (__key, __value) in #bi.iter() {
361                    if !__value.skip_serialization(#skip_serialization_attr) {
362                        ::serde::ser::SerializeMap::serialize_key(&mut __map_serializer, __key)?;
363                        ::serde::ser::SerializeMap::serialize_value(&mut __map_serializer, &::relay_protocol::SerializePayload(__value, #skip_serialization_attr))?;
364                    }
365                }
366            })
367            .to_tokens(&mut serialize_body);
368
369            (quote! {
370                for (__key, __value) in #bi.iter() {
371                    let __inner_tree = ::relay_protocol::IntoValue::extract_meta_tree(__value);
372                    if !__inner_tree.is_empty() {
373                        __child_meta.insert(__key.to_string(), __inner_tree);
374                    }
375                }
376            })
377            .to_tokens(&mut extract_child_meta_body);
378        } else if field_attrs.flatten {
379            if is_tuple_struct {
380                panic!("flatten not allowed in tuple struct");
381            }
382
383            (quote! {
384                let #bi = ::relay_protocol::FromObjectRef::from_object_ref(__obj);
385            })
386            .to_tokens(&mut from_value_body);
387
388            (quote! {
389                ::relay_protocol::IntoObjectRef::into_object_ref(#bi, __map);
390            })
391            .to_tokens(&mut to_value_body);
392
393            (quote! {
394                ::relay_protocol::IntoValue::serialize_payload(#bi, ::serde::__private::ser::FlatMapSerializer(&mut __map_serializer), __behavior)?;
395            }).to_tokens(&mut serialize_body);
396
397            (quote! {
398                __child_meta.extend(::relay_protocol::IntoValue::extract_child_meta(#bi));
399            })
400            .to_tokens(&mut extract_child_meta_body);
401        } else {
402            if is_tuple_struct {
403                (quote! {
404                    let #bi = __arr.next();
405                })
406                .to_tokens(&mut from_value_body);
407            } else {
408                (quote! {
409                    let #bi = __obj.remove(#field_name);
410                })
411                .to_tokens(&mut from_value_body);
412
413                for legacy_alias in &field_attrs.legacy_aliases {
414                    let legacy_field_name = LitStr::new(legacy_alias, Span::call_site());
415                    (quote! {
416                        let #bi = #bi.or(__obj.remove(#legacy_field_name));
417                    })
418                    .to_tokens(&mut from_value_body);
419                }
420            }
421
422            (quote! {
423                let #bi = ::relay_protocol::FromValue::from_value(#bi.unwrap_or_else(|| ::relay_protocol::Annotated(None, ::relay_protocol::Meta::default())));
424            }).to_tokens(&mut from_value_body);
425
426            if is_tuple_struct {
427                (quote! {
428                    __arr.push(Annotated::map_value(#bi, ::relay_protocol::IntoValue::into_value));
429                })
430                .to_tokens(&mut to_value_body);
431                (quote! {
432                    if !#bi.skip_serialization(#skip_serialization_attr) {
433                        ::serde::ser::SerializeSeq::serialize_element(&mut __seq_serializer, &::relay_protocol::SerializePayload(#bi, #skip_serialization_attr))?;
434                    }
435                }).to_tokens(&mut serialize_body);
436            } else {
437                (quote! {
438                    __map.insert(#field_name.to_string(), Annotated::map_value(#bi, ::relay_protocol::IntoValue::into_value));
439                }).to_tokens(&mut to_value_body);
440                (quote! {
441                    if !#bi.skip_serialization(#skip_serialization_attr) {
442                        ::serde::ser::SerializeMap::serialize_key(&mut __map_serializer, #field_name)?;
443                        ::serde::ser::SerializeMap::serialize_value(&mut __map_serializer, &::relay_protocol::SerializePayload(#bi, #skip_serialization_attr))?;
444                    }
445                }).to_tokens(&mut serialize_body);
446            }
447
448            (quote! {
449                let __inner_tree = ::relay_protocol::IntoValue::extract_meta_tree(#bi);
450                if !__inner_tree.is_empty() {
451                    __child_meta.insert(#field_name.to_string(), __inner_tree);
452                }
453            })
454            .to_tokens(&mut extract_child_meta_body);
455        }
456    }
457
458    let ast = s.ast();
459    let expectation = LitStr::new(&ast.ident.to_string().to_lowercase(), Span::call_site());
460    let mut variant = variant.clone();
461    for binding in variant.bindings_mut() {
462        binding.style = synstructure::BindStyle::Move;
463    }
464    let to_value_pat = variant.pat();
465    let to_structure_assemble_pat = variant.pat();
466    for binding in variant.bindings_mut() {
467        binding.style = synstructure::BindStyle::Ref;
468    }
469    let serialize_pat = variant.pat();
470
471    Ok(match t {
472        Trait::From => {
473            let bindings_count = variant.bindings().len();
474            if is_tuple_struct {
475                s.gen_impl(quote! {
476                    #[automatically_derived]
477                    gen impl ::relay_protocol::FromValue for @Self {
478                        fn from_value(
479                            __value: ::relay_protocol::Annotated<::relay_protocol::Value>,
480                        ) -> ::relay_protocol::Annotated<Self> {
481                            match __value {
482                                ::relay_protocol::Annotated(Some(::relay_protocol::Value::Array(mut __arr)), mut __meta) => {
483                                    if __arr.len() != #bindings_count {
484                                        __meta.add_error(Error::expected(concat!("a ", stringify!(#bindings_count), "-tuple")));
485                                        __meta.set_original_value(Some(__arr));
486                                        Annotated(None, __meta)
487                                    } else {
488                                        let mut __arr = __arr.into_iter();
489                                        #from_value_body;
490                                        ::relay_protocol::Annotated(Some(#to_structure_assemble_pat), __meta)
491                                    }
492                                }
493                                ::relay_protocol::Annotated(None, __meta) => ::relay_protocol::Annotated(None, __meta),
494                                ::relay_protocol::Annotated(Some(__value), mut __meta) => {
495                                    __meta.add_error(::relay_protocol::Error::expected(#expectation));
496                                    __meta.set_original_value(Some(__value));
497                                    ::relay_protocol::Annotated(None, __meta)
498                                }
499                            }
500                        }
501                    }
502                })
503            } else {
504                s.gen_impl(quote! {
505                    #[automatically_derived]
506                    gen impl ::relay_protocol::FromValue for @Self {
507                        fn from_value(
508                            mut __value: ::relay_protocol::Annotated<::relay_protocol::Value>,
509                        ) -> ::relay_protocol::Annotated<Self> {
510                            match __value {
511                                ::relay_protocol::Annotated(Some(::relay_protocol::Value::Object(ref mut __obj)), __meta) => {
512                                    #from_value_body;
513                                    ::relay_protocol::Annotated(Some(#to_structure_assemble_pat), __meta)
514                                },
515                                ::relay_protocol::Annotated(None, __meta) => ::relay_protocol::Annotated(None, __meta),
516                                ::relay_protocol::Annotated(Some(__value), mut __meta) => {
517                                    __meta.add_error(::relay_protocol::Error::expected(#expectation));
518                                    __meta.set_original_value(Some(__value));
519                                    ::relay_protocol::Annotated(None, __meta)
520                                }
521                            }
522                        }
523                    }
524
525                    #[automatically_derived]
526                    gen impl ::relay_protocol::FromObjectRef for @Self {
527                        fn from_object_ref(__obj: &mut relay_protocol::Object<::relay_protocol::Value>) -> Self {
528                            #from_value_body;
529                            #to_structure_assemble_pat
530                        }
531                    }
532                })
533            }
534        }
535        Trait::To => {
536            let into_value = if is_tuple_struct {
537                quote! {
538                    let mut __arr = ::relay_protocol::Array::new();
539                    let #to_value_pat = self;
540                    #to_value_body;
541                    ::relay_protocol::Value::Array(__arr)
542                }
543            } else {
544                quote! {
545                    let mut __map_ret = ::relay_protocol::Object::new();
546                    let #to_value_pat = self;
547                    let mut __map = &mut __map_ret;
548                    #to_value_body;
549                    ::relay_protocol::Value::Object(__map_ret)
550                }
551            };
552
553            let serialize_payload = if is_tuple_struct {
554                quote! {
555                    let mut __seq_serializer = ::serde::ser::Serializer::serialize_seq(__serializer, None)?;
556                    #serialize_body;
557                    ::serde::ser::SerializeSeq::end(__seq_serializer)
558                }
559            } else {
560                quote! {
561                    let mut __map_serializer = ::serde::ser::Serializer::serialize_map(__serializer, None)?;
562                    #serialize_body;
563                    ::serde::ser::SerializeMap::end(__map_serializer)
564                }
565            };
566
567            let into_value = s.gen_impl(quote! {
568                #[automatically_derived]
569                gen impl ::relay_protocol::IntoValue for @Self {
570                    fn into_value(self) -> ::relay_protocol::Value {
571                        #into_value
572                    }
573
574                    fn serialize_payload<S>(&self, __serializer: S, __behavior: ::relay_protocol::SkipSerialization) -> Result<S::Ok, S::Error>
575                    where
576                        Self: Sized,
577                        S: ::serde::ser::Serializer
578                    {
579                        let #serialize_pat = *self;
580                        #serialize_payload
581                    }
582
583                    fn extract_child_meta(&self) -> ::relay_protocol::MetaMap
584                    where
585                        Self: Sized,
586                    {
587                        let mut __child_meta = ::relay_protocol::MetaMap::new();
588                        let #serialize_pat = *self;
589                        #extract_child_meta_body;
590                        __child_meta
591                    }
592                }
593            });
594
595            let into_object_ref = (!is_tuple_struct).then(|| s.gen_impl(quote! {
596                #[automatically_derived]
597                gen impl ::relay_protocol::IntoObjectRef for @Self {
598                    fn into_object_ref(self, __map: &mut ::relay_protocol::Object<::relay_protocol::Value>) {
599                        let #to_value_pat = self;
600                        #to_value_body;
601                    }
602                }
603            }));
604
605            quote! {
606                #into_value
607                #into_object_ref
608            }
609        }
610    })
611}
612
613#[derive(Default)]
614struct TypeAttrs {
615    tag_key: Option<String>,
616}
617
618fn parse_type_attributes(s: &synstructure::Structure<'_>) -> syn::Result<TypeAttrs> {
619    let mut rv = TypeAttrs::default();
620
621    for attr in &s.ast().attrs {
622        if !attr.path().is_ident("metastructure") {
623            continue;
624        }
625
626        attr.parse_nested_meta(|meta| {
627            let ident = meta.path.require_ident()?;
628
629            if ident == "tag_key" {
630                let s = meta.value()?.parse::<LitStr>()?;
631                rv.tag_key = Some(s.value());
632            } else {
633                // Ignore other attributes used by `ProcessValue` derive macro.
634                let _ = meta.value()?.parse::<Lit>()?;
635            }
636
637            Ok(())
638        })?;
639    }
640
641    if rv.tag_key.is_some() && s.variants().len() == 1 {
642        // TODO: move into parse_type_attributes
643        return Err(Error::new(
644            s.ast().span(),
645            "tag_key not supported on structs",
646        ));
647    }
648
649    Ok(rv)
650}
651
652#[derive(Default)]
653struct FieldAttrs {
654    additional_properties: bool,
655    field_name: String,
656    flatten: bool,
657    legacy_aliases: Vec<String>,
658    skip_serialization: SkipSerialization,
659}
660
661#[derive(Copy, Clone, Default)]
662enum SkipSerialization {
663    #[default]
664    Never,
665    Null(bool),
666    Empty(bool),
667}
668
669impl SkipSerialization {
670    fn as_tokens(self) -> TokenStream {
671        match self {
672            SkipSerialization::Never => quote!(::relay_protocol::SkipSerialization::Never),
673            SkipSerialization::Null(deep) => {
674                quote!(::relay_protocol::SkipSerialization::Null(#deep))
675            }
676            SkipSerialization::Empty(deep) => {
677                quote!(::relay_protocol::SkipSerialization::Empty(#deep))
678            }
679        }
680    }
681}
682
683impl FromStr for SkipSerialization {
684    type Err = ();
685
686    fn from_str(s: &str) -> Result<Self, ()> {
687        Ok(match s {
688            "never" => SkipSerialization::Never,
689            "null" => SkipSerialization::Null(false),
690            "null_deep" => SkipSerialization::Null(true),
691            "empty" => SkipSerialization::Empty(false),
692            "empty_deep" => SkipSerialization::Empty(true),
693            _ => return Err(()),
694        })
695    }
696}
697
698fn parse_field_attributes(
699    index: usize,
700    bi_ast: &syn::Field,
701    is_tuple_struct: &mut bool,
702) -> syn::Result<FieldAttrs> {
703    if bi_ast.ident.is_none() {
704        *is_tuple_struct = true;
705    } else if *is_tuple_struct {
706        panic!("invalid tuple struct");
707    }
708
709    let mut rv = FieldAttrs::default();
710    if !*is_tuple_struct {
711        rv.skip_serialization = SkipSerialization::Null(false);
712    }
713    rv.field_name = bi_ast
714        .ident
715        .as_ref()
716        .map(ToString::to_string)
717        .unwrap_or_else(|| index.to_string());
718
719    for attr in &bi_ast.attrs {
720        if !attr.path().is_ident("metastructure") {
721            continue;
722        }
723
724        attr.parse_nested_meta(|meta| {
725            let ident = meta.path.require_ident()?;
726
727            if ident == "additional_properties" {
728                rv.additional_properties = true;
729            } else if ident == "omit_from_schema" {
730                // Skip
731            } else if ident == "field" {
732                rv.field_name = meta.value()?.parse::<LitStr>()?.value();
733            } else if ident == "flatten" {
734                rv.flatten = true;
735            } else if ident == "legacy_alias" {
736                rv.legacy_aliases
737                    .push(meta.value()?.parse::<LitStr>()?.value());
738            } else if ident == "skip_serialization" {
739                rv.skip_serialization = meta
740                    .value()?
741                    .parse::<LitStr>()?
742                    .value()
743                    .parse()
744                    .map_err(|_| meta.error("Unknown value"))?;
745            } else {
746                // Ignore other attributes used by `ProcessValue` derive macro.
747                let _ = meta.value()?.parse::<Lit>()?;
748            }
749
750            Ok(())
751        })?;
752    }
753
754    Ok(rv)
755}
756
757#[derive(Default)]
758struct VariantAttrs {
759    omit_from_schema: bool,
760    tag_override: Option<String>,
761    fallback_variant: bool,
762}
763
764fn parse_variant_attributes(attrs: &[syn::Attribute]) -> syn::Result<VariantAttrs> {
765    let mut rv = VariantAttrs::default();
766    for attr in attrs {
767        if !attr.path().is_ident("metastructure") {
768            continue;
769        }
770
771        attr.parse_nested_meta(|meta| {
772            let ident = meta.path.require_ident()?;
773
774            if ident == "fallback_variant" {
775                rv.tag_override = None;
776                rv.fallback_variant = true;
777            } else if ident == "omit_from_schema" {
778                rv.omit_from_schema = true;
779            } else if ident == "tag" {
780                rv.tag_override = Some(meta.value()?.parse::<LitStr>()?.value());
781            } else {
782                // Ignore other attributes used by `ProcessValue` derive macro.
783                let _ = meta.value()?.parse::<Lit>()?;
784            }
785
786            Ok(())
787        })?;
788    }
789
790    Ok(rv)
791}
792
793fn is_newtype_variant(variant: &synstructure::VariantInfo) -> bool {
794    variant.bindings().len() == 1 && variant.bindings()[0].ast().ident.is_none()
795}
796
797fn is_newtype(s: &synstructure::Structure<'_>) -> bool {
798    if s.variants().len() != 1 {
799        // We have more than one variant (e.g. `enum Foo { A, B }`)
800        return false;
801    }
802
803    if s.variants()[0].bindings().len() != 1 {
804        // The single variant has multiple fields
805        // e.g. `struct Foo(Bar, Baz)`
806        //      `enum Foo { A(X, Y) }`
807        return false;
808    }
809
810    if s.variants()[0].bindings()[0].ast().ident.is_some() {
811        // The variant has a name
812        // e.g. `struct Foo { bar: Bar }` instead of `struct Foo(Bar)`
813        return false;
814    }
815
816    true
817}