1use std::{borrow::Cow, fmt::Display};
220
221use heck::{
222 ToKebabCase, ToLowerCamelCase, ToPascalCase, ToShoutyKebabCase, ToShoutySnakeCase, ToSnakeCase,
223};
224use itertools::Itertools;
225use proc_macro2::{Span, TokenStream};
226use quote::{ToTokens, TokenStreamExt, format_ident, quote};
227use syn::{
228 Attribute, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident,
229 Lifetime, parse_macro_input,
230};
231
232#[proc_macro_derive(JsonPointee, attributes(ploidy))]
236pub fn derive_pointee(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
237 let input = parse_macro_input!(input as DeriveInput);
238 derive_pointee_for(&input)
239 .unwrap_or_else(|err| err.to_compile_error())
240 .into()
241}
242
243#[proc_macro_derive(JsonPointerTarget, attributes(ploidy))]
252pub fn derive_pointer_target(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
253 let input = parse_macro_input!(input as DeriveInput);
254 derive_pointer_target_for(&input)
255 .unwrap_or_else(|err| err.to_compile_error())
256 .into()
257}
258
259fn derive_pointee_for(input: &DeriveInput) -> syn::Result<TokenStream> {
260 let name = &input.ident;
261 let attrs = ContainerAttr::parse_all(&input.attrs)?;
262 let root = crate_path(&attrs);
263 let container = ContainerInfo::new(name, &root, &attrs)
264 .map_err(|err| syn::Error::new_spanned(input, err))?;
265
266 let pointer = Ident::new("pointer", Span::mixed_site());
268
269 let body = match &input.data {
270 Data::Struct(data) => {
271 if container.tag.is_some() {
272 return Err(syn::Error::new_spanned(input, DeriveError::TagOnNonEnum));
273 }
274 derive_for_struct(&pointer, container, data)?
275 }
276 Data::Enum(data) => derive_for_enum(&pointer, container, data)?,
277 Data::Union(_) => return Err(syn::Error::new_spanned(input, DeriveError::Union)),
278 };
279
280 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
281 let where_clause = {
282 let type_param_bounds = input
285 .generics
286 .params
287 .iter()
288 .filter_map(|param| match param {
289 GenericParam::Type(param) => {
290 let ident = ¶m.ident;
291 Some(quote! { #ident: #root::JsonPointee })
292 }
293 _ => None,
294 })
295 .collect_vec();
296 if type_param_bounds.is_empty() {
297 quote! { #where_clause }
298 } else if let Some(where_clause) = where_clause {
299 quote! { #where_clause #(#type_param_bounds),* }
300 } else {
301 quote! { where #(#type_param_bounds),* }
302 }
303 };
304
305 Ok(quote! {
306 #[automatically_derived]
307 impl #impl_generics #root::JsonPointee for #name #ty_generics #where_clause {
308 fn resolve(&self, #pointer: &#root::JsonPointer)
309 -> ::std::result::Result<&dyn #root::JsonPointee, #root::JsonPointeeError> {
310 #body
311 }
312 }
313 })
314}
315
316fn derive_pointer_target_for(input: &DeriveInput) -> syn::Result<TokenStream> {
317 let name = &input.ident;
318 let attrs = ContainerAttr::parse_all(&input.attrs)?;
319 let root = crate_path(&attrs);
320
321 let lifetime = Lifetime::new("'pointee", Span::mixed_site());
323
324 let (_, ty_generics, where_clause) = input.generics.split_for_impl();
325 let generics = {
326 let mut generics = input.generics.clone();
327 generics.params.push(syn::parse_quote!(#lifetime));
328 generics
329 };
330 let (impl_generics, _, _) = generics.split_for_impl();
331 let where_clause = match where_clause {
332 Some(where_clause) => {
334 let s = quote! { #name #ty_generics: ::std::any::Any };
335 quote! { #where_clause, #s }
336 }
337 None => quote! { where #name #ty_generics: ::std::any::Any },
338 };
339
340 Ok(quote! {
341 #[automatically_derived]
342 impl #impl_generics #root::JsonPointerTarget<#lifetime>
343 for &#lifetime #name #ty_generics
344 #where_clause
345 {
346 #[inline]
347 fn from_pointee(
348 pointee: &#lifetime dyn #root::JsonPointee,
349 ) -> ::std::result::Result<Self, #root::JsonPointerTargetError> {
350 let any: &dyn ::std::any::Any = pointee;
351 any.downcast_ref().ok_or_else(|| #root::JsonPointerTargetError {
352 expected: ::std::any::type_name::<#name #ty_generics>(),
353 actual: pointee.name(),
354 })
355 }
356 }
357 })
358}
359
360fn derive_for_struct(
361 pointer: &Ident,
362 container: ContainerInfo<'_>,
363 data: &DataStruct,
364) -> syn::Result<TokenStream> {
365 let body = match &data.fields {
366 Fields::Named(fields) => {
367 let fields: Vec<_> = fields
368 .named
369 .iter()
370 .map(|f| NamedFieldInfo::new(container, f))
371 .try_collect()?;
372 let bindings = fields.iter().map(|f| {
373 let binding = f.binding;
374 quote! { #binding }
375 });
376 let body = NamedPointeeBody::new(NamedPointeeTy::Struct(container), pointer, &fields);
377 quote! {
378 let Self { #(#bindings),* } = self;
379 #body
380 }
381 }
382 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
383 let root = container.root;
385 quote! {
386 <_ as #root::JsonPointee>::resolve(&self.0, #pointer)
387 }
388 }
389 Fields::Unnamed(fields) => {
390 let fields: Vec<_> = fields
391 .unnamed
392 .iter()
393 .enumerate()
394 .map(|(index, f)| TupleFieldInfo::new(index, f))
395 .try_collect()?;
396 let bindings = fields.iter().map(|f| {
397 let binding = &f.binding;
398 quote! { #binding }
399 });
400 let body = TuplePointeeBody::new(TuplePointeeTy::Struct(container), pointer, &fields);
401 quote! {
402 let Self(#(#bindings),*) = self;
403 #body
404 }
405 }
406 Fields::Unit => {
407 let body = UnitPointeeBody::new(UnitPointeeTy::Struct(container), pointer);
408 quote!(#body)
409 }
410 };
411 Ok(body)
412}
413
414fn derive_for_enum(
415 pointer: &Ident,
416 container: ContainerInfo<'_>,
417 data: &DataEnum,
418) -> syn::Result<TokenStream> {
419 let tag = container.tag.unwrap_or(VariantTag::External);
422
423 let arms: Vec<_> = data
424 .variants
425 .iter()
426 .map(|variant| {
427 let name = &variant.ident;
428 let root = container.root;
429 let attrs: Vec<_> = variant
430 .attrs
431 .iter()
432 .map(VariantAttr::parse_one)
433 .flatten_ok()
434 .try_collect()?;
435 let info = VariantInfo::new(container, name, &attrs);
436
437 if info.is_skipped() {
440 let ty = match &variant.fields {
441 Fields::Named(_) => VariantTy::Named(info, tag),
442 Fields::Unnamed(_) => VariantTy::Tuple(info, tag),
443 Fields::Unit => VariantTy::Unit(info, tag),
444 };
445 let body = SkippedVariantBody::new(ty, pointer);
446 return syn::Result::Ok(quote!(#body));
447 }
448
449 let arm = match &variant.fields {
450 Fields::Named(fields) => {
451 let fields: Vec<_> = fields
452 .named
453 .iter()
454 .map(|f| NamedFieldInfo::new(container, f))
455 .try_collect()?;
456 let bindings = fields.iter().map(|f| {
457 let binding = f.binding;
458 quote! { #binding }
459 });
460 let body = NamedPointeeBody::new(
461 NamedPointeeTy::Variant(info, tag),
462 pointer,
463 &fields,
464 );
465 quote! {
466 Self::#name { #(#bindings),* } => {
467 #body
468 }
469 }
470 }
471 Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
472 match tag {
473 VariantTag::Internal(tag_field) => {
474 let key = Ident::new("key", Span::mixed_site());
477 let effective_name = info.effective_name();
478 quote! {
479 Self::#name(inner) => {
480 let Some(#key) = #pointer.head() else {
481 return Ok(self as &dyn #root::JsonPointee);
482 };
483 if #key == #tag_field {
484 return Ok(&#effective_name as &dyn #root::JsonPointee);
485 }
486 <_ as #root::JsonPointee>::resolve(inner, #pointer)
487 }
488 }
489 }
490 VariantTag::External => {
491 let key = Ident::new("key", Span::mixed_site());
495 let effective_name = info.effective_name();
496 let pointee_ty = TuplePointeeTy::Variant(info, tag);
497 let key_err = if cfg!(feature = "did-you-mean") {
498 quote!(#root::JsonPointerKeyError::with_ty(#key, #pointee_ty))
499 } else {
500 quote!(#root::JsonPointerKeyError::new(#key))
501 };
502 quote! {
503 Self::#name(inner) => {
504 let Some(#key) = #pointer.head() else {
505 return Ok(self as &dyn #root::JsonPointee);
506 };
507 if #key != #effective_name {
508 return Err(#key_err)?;
509 }
510 <_ as #root::JsonPointee>::resolve(inner, #pointer.tail())
511 }
512 }
513 }
514 VariantTag::Adjacent { tag: tag_field, content: content_field } => {
515 let key = Ident::new("key", Span::mixed_site());
518 let effective_name = info.effective_name();
519 let pointee_ty = TuplePointeeTy::Variant(info, tag);
520 let key_err = if cfg!(feature = "did-you-mean") {
521 quote!(#root::JsonPointerKeyError::with_suggestions(
522 #key,
523 #pointee_ty,
524 [#tag_field, #content_field],
525 ))
526 } else {
527 quote!(#root::JsonPointerKeyError::new(#key))
528 };
529 quote! {
530 Self::#name(inner) => {
531 let Some(#key) = #pointer.head() else {
532 return Ok(self as &dyn #root::JsonPointee);
533 };
534 match &*#key.to_str() {
535 #tag_field => Ok(&#effective_name as &dyn #root::JsonPointee),
536 #content_field => <_ as #root::JsonPointee>::resolve(inner, #pointer.tail()),
537 _ => Err(#key_err)?,
538 }
539 }
540 }
541 }
542 VariantTag::Untagged => {
543 quote! {
546 Self::#name(inner) => {
547 <_ as #root::JsonPointee>::resolve(
548 inner,
549 #pointer,
550 )
551 }
552 }
553 }
554 }
555 }
556 Fields::Unnamed(fields) => {
557 let fields: Vec<_> = fields
558 .unnamed
559 .iter()
560 .enumerate()
561 .map(|(index, f)| TupleFieldInfo::new(index, f))
562 .try_collect()?;
563 let bindings = fields.iter().map(|f| {
564 let binding = &f.binding;
565 quote! { #binding }
566 });
567 let body = TuplePointeeBody::new(
568 TuplePointeeTy::Variant(info, tag),
569 pointer,
570 &fields,
571 );
572 quote! {
573 Self::#name(#(#bindings),*) => {
574 #body
575 }
576 }
577 }
578 Fields::Unit => {
579 let body = UnitPointeeBody::new(
580 UnitPointeeTy::Variant(info, tag),
581 pointer,
582 );
583 quote! {
584 Self::#name => {
585 #body
586 }
587 }
588 }
589 };
590 syn::Result::Ok(arm)
591 })
592 .try_collect()?;
593
594 Ok(quote! {
595 match self {
596 #(#arms,)*
597 }
598 })
599}
600
601fn crate_path(attrs: &[ContainerAttr]) -> Cow<'_, syn::Path> {
604 attrs
605 .iter()
606 .find_map(|attr| match attr {
607 ContainerAttr::Crate(path) => Some(Cow::Borrowed(path)),
608 _ => None,
609 })
610 .unwrap_or_else(|| Cow::Owned(syn::parse_quote!(::ploidy_pointer)))
611}
612
613#[derive(Clone, Copy)]
614struct ContainerInfo<'a> {
615 name: &'a Ident,
616 root: &'a syn::Path,
617 rename_all: Option<RenameAll>,
618 tag: Option<VariantTag<'a>>,
619}
620
621impl<'a> ContainerInfo<'a> {
622 fn new(
623 name: &'a Ident,
624 root: &'a syn::Path,
625 attrs: &'a [ContainerAttr],
626 ) -> Result<Self, DeriveError> {
627 let rename_all = attrs.iter().find_map(|attr| match attr {
628 &ContainerAttr::RenameAll(rename_all) => Some(rename_all),
629 _ => None,
630 });
631
632 let tag = attrs
633 .iter()
634 .filter_map(|attr| match attr {
635 ContainerAttr::Tag(t) => Some(t.as_str()),
636 _ => None,
637 })
638 .at_most_one()
639 .map_err(|_| DeriveError::ConflictingTagAttributes)?;
640 let content = attrs
641 .iter()
642 .filter_map(|attr| match attr {
643 ContainerAttr::Content(c) => Some(c.as_str()),
644 _ => None,
645 })
646 .at_most_one()
647 .map_err(|_| DeriveError::ConflictingTagAttributes)?;
648 let untagged = attrs
649 .iter()
650 .filter(|attr| matches!(attr, ContainerAttr::Untagged))
651 .at_most_one()
652 .map_err(|_| DeriveError::ConflictingTagAttributes)?;
653 let tag = match (tag, content, untagged) {
654 (None, None, None) => None,
656 (Some(tag), None, None) => Some(VariantTag::Internal(tag)),
658 (None, None, Some(_)) => Some(VariantTag::Untagged),
660 (Some(tag), Some(content), None) if tag == content => {
661 return Err(DeriveError::SameTagAndContent);
662 }
663 (Some(tag), Some(content), None) => Some(VariantTag::Adjacent { tag, content }),
665 (None, Some(_), _) => return Err(DeriveError::ContentWithoutTag),
666 _ => return Err(DeriveError::ConflictingTagAttributes),
667 };
668
669 Ok(Self {
670 name,
671 root,
672 rename_all,
673 tag,
674 })
675 }
676}
677
678#[derive(Debug)]
679struct NamedFieldInfo<'a> {
680 binding: &'a Ident,
681 key: String,
682 is_flattened: bool,
683 is_skipped: bool,
684}
685
686impl<'a> NamedFieldInfo<'a> {
687 fn new(container: ContainerInfo<'a>, f: &'a Field) -> syn::Result<Self> {
688 let name = f.ident.as_ref().unwrap();
689 let attrs: Vec<_> = f
690 .attrs
691 .iter()
692 .map(FieldAttr::parse_one)
693 .flatten_ok()
694 .try_collect()?;
695
696 let is_flattened = attrs.iter().any(|attr| matches!(attr, FieldAttr::Flatten));
697 let is_skipped = attrs.iter().any(|attr| matches!(attr, FieldAttr::Skip));
698
699 if is_flattened && is_skipped {
700 return Err(syn::Error::new_spanned(f, DeriveError::FlattenWithSkip));
701 }
702
703 let key = attrs
704 .iter()
705 .find_map(|attr| match attr {
706 FieldAttr::Rename(name) => Some(name.clone()),
707 _ => None,
708 })
709 .or_else(|| {
710 container
711 .rename_all
712 .map(|rename_all| rename_all.apply(&name.to_string()))
713 })
714 .unwrap_or_else(|| name.to_string());
715
716 Ok(NamedFieldInfo {
717 binding: name,
718 key,
719 is_flattened,
720 is_skipped,
721 })
722 }
723}
724
725#[derive(Debug)]
726struct TupleFieldInfo {
727 index: usize,
728 binding: Ident,
729 is_skipped: bool,
730}
731
732impl TupleFieldInfo {
733 fn new(index: usize, f: &Field) -> syn::Result<Self> {
734 let attrs: Vec<_> = f
735 .attrs
736 .iter()
737 .map(FieldAttr::parse_one)
738 .flatten_ok()
739 .try_collect()?;
740
741 let _: () = attrs
742 .iter()
743 .map(|attr| match attr {
744 FieldAttr::Flatten => {
745 Err(syn::Error::new_spanned(f, DeriveError::FlattenOnNonNamed))
746 }
747 FieldAttr::Rename(_) => {
748 Err(syn::Error::new_spanned(f, DeriveError::RenameOnNonNamed))
749 }
750 _ => Ok(()),
751 })
752 .try_collect()?;
753
754 let is_skipped = attrs.iter().any(|attr| matches!(attr, FieldAttr::Skip));
755
756 Ok(Self {
757 index,
758 binding: format_ident!("f{}", index, span = Span::mixed_site()),
759 is_skipped,
760 })
761 }
762}
763
764#[derive(Clone, Copy)]
765struct VariantInfo<'a> {
766 container: ContainerInfo<'a>,
767 name: &'a Ident,
768 attrs: &'a [VariantAttr],
769}
770
771impl<'a> VariantInfo<'a> {
772 fn new(container: ContainerInfo<'a>, name: &'a Ident, attrs: &'a [VariantAttr]) -> Self {
773 Self {
774 container,
775 name,
776 attrs,
777 }
778 }
779
780 fn effective_name(&self) -> String {
781 self.attrs
782 .iter()
783 .find_map(|attr| match attr {
784 VariantAttr::Rename(name) => Some(name.clone()),
785 _ => None,
786 })
787 .or_else(|| {
788 self.container
789 .rename_all
790 .map(|rename_all| rename_all.apply(&self.name.to_string()))
791 })
792 .unwrap_or_else(|| self.name.to_string())
793 }
794
795 fn is_skipped(&self) -> bool {
796 self.attrs
797 .iter()
798 .any(|attr| matches!(attr, VariantAttr::Skip))
799 }
800}
801
802#[derive(Clone, Copy)]
803struct NamedPointeeBody<'a> {
804 ty: NamedPointeeTy<'a>,
805 pointer: &'a Ident,
806 fields: &'a [NamedFieldInfo<'a>],
807}
808
809impl<'a> NamedPointeeBody<'a> {
810 fn new(ty: NamedPointeeTy<'a>, pointer: &'a Ident, fields: &'a [NamedFieldInfo]) -> Self {
811 Self {
812 ty,
813 pointer,
814 fields,
815 }
816 }
817}
818
819impl ToTokens for NamedPointeeBody<'_> {
820 fn to_tokens(&self, tokens: &mut TokenStream) {
821 let root = self.ty.container().root;
822 let pointer = self.pointer;
823 let key = Ident::new("key", Span::mixed_site());
824 let pointee_ty = self.ty;
825
826 let arms = self
828 .fields
829 .iter()
830 .filter(|f| !f.is_flattened && !f.is_skipped)
831 .map(|f| {
832 let field_key = &f.key;
833 let binding = f.binding;
834 quote! {
835 #field_key => <_ as #root::JsonPointee>::resolve(
836 #binding,
837 #pointer.tail(),
838 )
839 }
840 });
841
842 let mut suggestions: Vec<_> = self
844 .fields
845 .iter()
846 .filter(|f| !f.is_flattened && !f.is_skipped)
847 .map(|f| {
848 let key = &f.key;
849 quote! { #key }
850 })
851 .collect();
852 if let NamedPointeeTy::Variant(_, VariantTag::Internal(tag)) = self.ty {
853 suggestions.push(quote! { #tag });
854 }
855
856 let wildcard = {
857 let rest = if cfg!(feature = "did-you-mean") {
860 quote!(Err(#root::JsonPointerKeyError::with_suggestions(
861 #key,
862 #pointee_ty,
863 [#(#suggestions),*],
864 ))?)
865 } else {
866 quote!(Err(#root::JsonPointerKeyError::new(#key))?)
867 };
868 self.fields
869 .iter()
870 .filter(|f| f.is_flattened)
871 .rfold(rest, |rest, f| {
872 let binding = f.binding;
873 quote! {
874 <_ as #root::JsonPointee>
875 ::resolve(
876 #binding,
877 #pointer
878 )
879 .or_else(|_| #rest)
880 }
881 })
882 };
883
884 let body = match self.ty {
885 NamedPointeeTy::Variant(info, VariantTag::Internal(tag_field)) => {
886 let variant_name = info.effective_name();
889 quote! {
890 let Some(#key) = #pointer.head() else {
891 return Ok(self as &dyn #root::JsonPointee);
892 };
893 if #key == #tag_field {
894 return Ok(&#variant_name as &dyn #root::JsonPointee);
895 }
896 match &*#key.to_str() {
897 #(#arms,)*
898 _ => #wildcard,
899 }
900 }
901 }
902 NamedPointeeTy::Variant(info, VariantTag::External) => {
903 let variant_name = info.effective_name();
907 let ty_err = if cfg!(feature = "did-you-mean") {
908 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #pointee_ty))
909 } else {
910 quote!(#root::JsonPointerTypeError::new(&#pointer))
911 };
912 quote! {
913 let Some(#key) = #pointer.head() else {
914 return Ok(self as &dyn #root::JsonPointee);
915 };
916 if #key != #variant_name {
917 return Err(#ty_err)?;
918 }
919 let #pointer = #pointer.tail();
920 let Some(#key) = #pointer.head() else {
921 return Ok(self as &dyn #root::JsonPointee);
922 };
923 match &*#key.to_str() {
924 #(#arms,)*
925 _ => #wildcard,
926 }
927 }
928 }
929 NamedPointeeTy::Variant(
930 info,
931 VariantTag::Adjacent {
932 tag: tag_field,
933 content: content_field,
934 },
935 ) => {
936 let variant_name = info.effective_name();
939 let key_err = if cfg!(feature = "did-you-mean") {
940 quote!(#root::JsonPointerKeyError::with_suggestions(
941 #key,
942 #pointee_ty,
943 [#tag_field, #content_field],
944 ))
945 } else {
946 quote!(#root::JsonPointerKeyError::new(#key))
947 };
948 quote! {
949 let Some(#key) = #pointer.head() else {
950 return Ok(self as &dyn #root::JsonPointee);
951 };
952 match &*#key.to_str() {
953 #tag_field => {
954 return Ok(&#variant_name as &dyn #root::JsonPointee);
955 }
956 #content_field => {
957 let #pointer = #pointer.tail();
958 let Some(#key) = #pointer.head() else {
959 return Ok(self as &dyn #root::JsonPointee);
960 };
961 match &*#key.to_str() {
962 #(#arms,)*
963 _ => #wildcard,
964 }
965 }
966 _ => {
967 return Err(#key_err)?;
968 }
969 }
970 }
971 }
972 NamedPointeeTy::Struct(_) | NamedPointeeTy::Variant(_, VariantTag::Untagged) => {
973 quote! {
976 let Some(#key) = #pointer.head() else {
977 return Ok(self as &dyn #root::JsonPointee);
978 };
979 match &*#key.to_str() {
980 #(#arms,)*
981 _ => #wildcard,
982 }
983 }
984 }
985 };
986
987 tokens.append_all(body);
988 }
989}
990
991#[derive(Clone, Copy)]
992struct TuplePointeeBody<'a> {
993 ty: TuplePointeeTy<'a>,
994 pointer: &'a Ident,
995 fields: &'a [TupleFieldInfo],
996}
997
998impl<'a> TuplePointeeBody<'a> {
999 fn new(ty: TuplePointeeTy<'a>, pointer: &'a Ident, fields: &'a [TupleFieldInfo]) -> Self {
1000 Self {
1001 ty,
1002 pointer,
1003 fields,
1004 }
1005 }
1006}
1007
1008impl ToTokens for TuplePointeeBody<'_> {
1009 fn to_tokens(&self, tokens: &mut TokenStream) {
1010 let root = self.ty.container().root;
1011 let pointer = self.pointer;
1012 let idx = Ident::new("idx", Span::mixed_site());
1013 let key = Ident::new("key", Span::mixed_site());
1014
1015 let arms = self.fields.iter().filter(|f| !f.is_skipped).map(|f| {
1017 let index = f.index;
1018 let binding = &f.binding;
1019 quote! {
1020 #index => <_ as #root::JsonPointee>::resolve(
1021 #binding,
1022 #pointer.tail(),
1023 )
1024 }
1025 });
1026
1027 let ty = self.ty;
1029 let len = self.fields.len();
1030 let ty_err = if cfg!(feature = "did-you-mean") {
1031 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1032 } else {
1033 quote!(#root::JsonPointerTypeError::new(&#pointer))
1034 };
1035 let tail = quote! {
1036 let Some(#idx) = #key.to_index() else {
1037 return Err(#ty_err)?;
1038 };
1039 match #idx {
1040 #(#arms,)*
1041 _ => Err(#root::JsonPointeeError::Index(#idx, 0..#len))
1042 }
1043 };
1044
1045 let body = match self.ty {
1046 TuplePointeeTy::Variant(info, VariantTag::Internal(tag_field)) => {
1047 let variant_name = info.effective_name();
1050 quote! {
1051 let Some(#key) = #pointer.head() else {
1052 return Ok(self as &dyn #root::JsonPointee);
1053 };
1054 if #key == #tag_field {
1055 return Ok(&#variant_name as &dyn #root::JsonPointee);
1056 }
1057 #tail
1058 }
1059 }
1060 TuplePointeeTy::Variant(info, VariantTag::External) => {
1061 let variant_name = info.effective_name();
1065 let ty_err = if cfg!(feature = "did-you-mean") {
1066 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1067 } else {
1068 quote!(#root::JsonPointerTypeError::new(&#pointer))
1069 };
1070 quote! {
1071 let Some(#key) = #pointer.head() else {
1072 return Ok(self as &dyn #root::JsonPointee);
1073 };
1074 if #key != #variant_name {
1075 return Err(#ty_err)?;
1076 }
1077 let #pointer = #pointer.tail();
1078 let Some(#key) = #pointer.head() else {
1079 return Ok(self as &dyn #root::JsonPointee);
1080 };
1081 #tail
1082 }
1083 }
1084 TuplePointeeTy::Variant(
1085 info,
1086 VariantTag::Adjacent {
1087 tag: tag_field,
1088 content: content_field,
1089 },
1090 ) => {
1091 let variant_name = info.effective_name();
1094 let key_err = if cfg!(feature = "did-you-mean") {
1095 quote!(#root::JsonPointerKeyError::with_suggestions(
1096 #key,
1097 #ty,
1098 [#tag_field, #content_field],
1099 ))
1100 } else {
1101 quote!(#root::JsonPointerKeyError::new(#key))
1102 };
1103 quote! {
1104 let Some(#key) = #pointer.head() else {
1105 return Ok(self as &dyn #root::JsonPointee);
1106 };
1107 match &*#key.to_str() {
1108 #tag_field => {
1109 return Ok(&#variant_name as &dyn #root::JsonPointee);
1110 }
1111 #content_field => {
1112 let #pointer = #pointer.tail();
1113 let Some(#key) = #pointer.head() else {
1114 return Ok(self as &dyn #root::JsonPointee);
1115 };
1116 #tail
1117 }
1118 _ => {
1119 return Err(#key_err)?;
1120 }
1121 }
1122 }
1123 }
1124 TuplePointeeTy::Struct(_) | TuplePointeeTy::Variant(_, VariantTag::Untagged) => {
1125 quote! {
1128 let Some(#key) = #pointer.head() else {
1129 return Ok(self as &dyn #root::JsonPointee);
1130 };
1131 #tail
1132 }
1133 }
1134 };
1135
1136 tokens.append_all(body);
1137 }
1138}
1139
1140#[derive(Clone, Copy)]
1141struct UnitPointeeBody<'a> {
1142 ty: UnitPointeeTy<'a>,
1143 pointer: &'a Ident,
1144}
1145
1146impl<'a> UnitPointeeBody<'a> {
1147 fn new(ty: UnitPointeeTy<'a>, pointer: &'a Ident) -> Self {
1148 Self { ty, pointer }
1149 }
1150}
1151
1152impl ToTokens for UnitPointeeBody<'_> {
1153 fn to_tokens(&self, tokens: &mut TokenStream) {
1154 let root = self.ty.container().root;
1155 let pointer = self.pointer;
1156 let body = match self.ty {
1157 ty @ UnitPointeeTy::Variant(info, VariantTag::Internal(tag_field)) => {
1158 let key = Ident::new("key", Span::mixed_site());
1160 let variant_name = info.effective_name();
1161 let key_err = if cfg!(feature = "did-you-mean") {
1162 quote!(#root::JsonPointerKeyError::with_suggestions(
1163 #key,
1164 #ty,
1165 [#tag_field],
1166 ))
1167 } else {
1168 quote!(#root::JsonPointerKeyError::new(#key))
1169 };
1170 quote! {
1171 let Some(#key) = #pointer.head() else {
1172 return Ok(self as &dyn #root::JsonPointee);
1173 };
1174 if #key == #tag_field {
1175 return Ok(&#variant_name as &dyn #root::JsonPointee);
1176 }
1177 Err(#key_err)?
1178 }
1179 }
1180 ty @ UnitPointeeTy::Variant(info, VariantTag::External) => {
1181 let key = Ident::new("key", Span::mixed_site());
1183 let variant_name = info.effective_name();
1184 let key_err = if cfg!(feature = "did-you-mean") {
1185 quote!(#root::JsonPointerKeyError::with_ty(#key, #ty))
1186 } else {
1187 quote!(#root::JsonPointerKeyError::new(#key))
1188 };
1189 let ty_err = if cfg!(feature = "did-you-mean") {
1190 quote!(#root::JsonPointerTypeError::with_ty(&#pointer.tail(), #ty))
1191 } else {
1192 quote!(#root::JsonPointerTypeError::new(&#pointer.tail()))
1193 };
1194 quote! {
1195 let Some(#key) = #pointer.head() else {
1196 return Ok(self as &dyn #root::JsonPointee);
1197 };
1198 if #key != #variant_name {
1199 return Err(#key_err)?;
1200 }
1201 if !#pointer.tail().is_empty() {
1202 return Err(#ty_err)?;
1203 }
1204 Ok(self as &dyn #root::JsonPointee)
1205 }
1206 }
1207 ty @ UnitPointeeTy::Variant(info, VariantTag::Adjacent { tag: tag_field, .. }) => {
1208 let key = Ident::new("key", Span::mixed_site());
1210 let variant_name = info.effective_name();
1211 let key_err = if cfg!(feature = "did-you-mean") {
1212 quote!(#root::JsonPointerKeyError::with_suggestions(
1213 #key,
1214 #ty,
1215 [#tag_field],
1216 ))
1217 } else {
1218 quote!(#root::JsonPointerKeyError::new(#key))
1219 };
1220 quote! {
1221 let Some(#key) = #pointer.head() else {
1222 return Ok(self as &dyn #root::JsonPointee);
1223 };
1224 match &*#key.to_str() {
1225 #tag_field => {
1226 return Ok(&#variant_name as &dyn #root::JsonPointee);
1227 }
1228 _ => {
1229 return Err(#key_err)?;
1230 }
1231 }
1232 }
1233 }
1234 ty @ (UnitPointeeTy::Struct(_) | UnitPointeeTy::Variant(_, VariantTag::Untagged)) => {
1235 let ty_err = if cfg!(feature = "did-you-mean") {
1237 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1238 } else {
1239 quote!(#root::JsonPointerTypeError::new(&#pointer))
1240 };
1241 quote! {
1242 if #pointer.is_empty() {
1243 Ok(self as &dyn #root::JsonPointee)
1244 } else {
1245 Err(#ty_err)?
1246 }
1247 }
1248 }
1249 };
1250 tokens.append_all(body);
1251 }
1252}
1253
1254#[derive(Clone, Copy)]
1255enum NamedPointeeTy<'a> {
1256 Struct(ContainerInfo<'a>),
1257 Variant(VariantInfo<'a>, VariantTag<'a>),
1258}
1259
1260impl<'a> NamedPointeeTy<'a> {
1261 fn container(self) -> ContainerInfo<'a> {
1262 match self {
1263 Self::Struct(info) => info,
1264 Self::Variant(info, _) => info.container,
1265 }
1266 }
1267}
1268
1269impl ToTokens for NamedPointeeTy<'_> {
1270 fn to_tokens(&self, tokens: &mut TokenStream) {
1271 let root = self.container().root;
1272 tokens.append_all(match self {
1273 Self::Struct(info) => {
1274 let ty = info.name;
1275 quote! {
1276 #root::JsonPointeeType::struct_named(
1277 stringify!(#ty)
1278 )
1279 }
1280 }
1281 Self::Variant(info, ..) => {
1282 let ty = info.container.name;
1283 let variant = info.name;
1284 quote! {
1285 #root::JsonPointeeType::struct_variant_named(
1286 stringify!(#ty),
1287 stringify!(#variant),
1288 )
1289 }
1290 }
1291 });
1292 }
1293}
1294
1295#[derive(Clone, Copy)]
1296enum TuplePointeeTy<'a> {
1297 Struct(ContainerInfo<'a>),
1298 Variant(VariantInfo<'a>, VariantTag<'a>),
1299}
1300
1301impl<'a> TuplePointeeTy<'a> {
1302 fn container(self) -> ContainerInfo<'a> {
1303 match self {
1304 Self::Struct(info) => info,
1305 Self::Variant(info, _) => info.container,
1306 }
1307 }
1308}
1309
1310impl ToTokens for TuplePointeeTy<'_> {
1311 fn to_tokens(&self, tokens: &mut TokenStream) {
1312 let root = self.container().root;
1313 tokens.append_all(match self {
1314 Self::Struct(info) => {
1315 let ty = info.name;
1316 quote! {
1317 #root::JsonPointeeType::tuple_struct_named(
1318 stringify!(#ty)
1319 )
1320 }
1321 }
1322 Self::Variant(info, ..) => {
1323 let ty = info.container.name;
1324 let variant = info.name;
1325 quote! {
1326 #root::JsonPointeeType::tuple_variant_named(
1327 stringify!(#ty),
1328 stringify!(#variant),
1329 )
1330 }
1331 }
1332 });
1333 }
1334}
1335
1336#[derive(Clone, Copy)]
1337enum UnitPointeeTy<'a> {
1338 Struct(ContainerInfo<'a>),
1339 Variant(VariantInfo<'a>, VariantTag<'a>),
1340}
1341
1342impl<'a> UnitPointeeTy<'a> {
1343 fn container(self) -> ContainerInfo<'a> {
1344 match self {
1345 Self::Struct(info) => info,
1346 Self::Variant(info, _) => info.container,
1347 }
1348 }
1349}
1350
1351impl ToTokens for UnitPointeeTy<'_> {
1352 fn to_tokens(&self, tokens: &mut TokenStream) {
1353 let root = self.container().root;
1354 tokens.append_all(match self {
1355 Self::Struct(info) => {
1356 let ty = info.name;
1357 quote! {
1358 #root::JsonPointeeType::unit_struct_named(
1359 stringify!(#ty)
1360 )
1361 }
1362 }
1363 Self::Variant(info, ..) => {
1364 let ty = info.container.name;
1365 let variant = info.name;
1366 quote! {
1367 #root::JsonPointeeType::unit_variant_named(
1368 stringify!(#ty),
1369 stringify!(#variant),
1370 )
1371 }
1372 }
1373 });
1374 }
1375}
1376
1377#[derive(Clone, Copy)]
1378enum VariantTy<'a> {
1379 Named(VariantInfo<'a>, VariantTag<'a>),
1380 Tuple(VariantInfo<'a>, VariantTag<'a>),
1381 Unit(VariantInfo<'a>, VariantTag<'a>),
1382}
1383
1384impl<'a> VariantTy<'a> {
1385 fn info(self) -> VariantInfo<'a> {
1386 let (Self::Named(info, _) | Self::Tuple(info, _) | Self::Unit(info, _)) = self;
1387 info
1388 }
1389
1390 fn tag(self) -> VariantTag<'a> {
1391 let (Self::Named(_, tag) | Self::Tuple(_, tag) | Self::Unit(_, tag)) = self;
1392 tag
1393 }
1394}
1395
1396impl ToTokens for VariantTy<'_> {
1397 fn to_tokens(&self, tokens: &mut TokenStream) {
1398 let root = self.info().container.root;
1399 tokens.append_all(match self {
1400 Self::Named(info, _) => {
1401 let ty = info.container.name;
1402 let variant = info.name;
1403 quote! {
1404 #root::JsonPointeeType::struct_variant_named(
1405 stringify!(#ty),
1406 stringify!(#variant),
1407 )
1408 }
1409 }
1410 Self::Tuple(info, _) => {
1411 let ty = info.container.name;
1412 let variant = info.name;
1413 quote! {
1414 #root::JsonPointeeType::tuple_variant_named(
1415 stringify!(#ty),
1416 stringify!(#variant),
1417 )
1418 }
1419 }
1420 Self::Unit(info, _) => {
1421 let ty = info.container.name;
1422 let variant = info.name;
1423 quote! {
1424 #root::JsonPointeeType::unit_variant_named(
1425 stringify!(#ty),
1426 stringify!(#variant),
1427 )
1428 }
1429 }
1430 });
1431 }
1432}
1433
1434#[derive(Clone, Copy)]
1435struct SkippedVariantBody<'a> {
1436 ty: VariantTy<'a>,
1437 pointer: &'a Ident,
1438}
1439
1440impl<'a> SkippedVariantBody<'a> {
1441 fn new(ty: VariantTy<'a>, pointer: &'a Ident) -> Self {
1442 Self { ty, pointer }
1443 }
1444}
1445
1446impl ToTokens for SkippedVariantBody<'_> {
1447 fn to_tokens(&self, tokens: &mut TokenStream) {
1448 let root = self.ty.info().container.root;
1449 let pointer = self.pointer;
1450 let ty = self.ty;
1451
1452 let pattern = match ty {
1453 VariantTy::Named(info, _) => {
1454 let variant_name = info.name;
1455 quote!(Self::#variant_name { .. })
1456 }
1457 VariantTy::Tuple(info, _) => {
1458 let variant_name = info.name;
1459 quote!(Self::#variant_name(..))
1460 }
1461 VariantTy::Unit(info, _) => {
1462 let variant_name = info.name;
1463 quote!(Self::#variant_name)
1464 }
1465 };
1466
1467 match ty.tag() {
1468 VariantTag::Internal(tag_field) => {
1469 let key = Ident::new("key", Span::mixed_site());
1471 let effective_name = ty.info().effective_name();
1472 let ty_err = if cfg!(feature = "did-you-mean") {
1473 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1474 } else {
1475 quote!(#root::JsonPointerTypeError::new(&#pointer))
1476 };
1477 tokens.append_all(quote! {
1478 #pattern => {
1479 let Some(#key) = #pointer.head() else {
1480 return Ok(self as &dyn #root::JsonPointee);
1481 };
1482 if #key == #tag_field {
1483 return Ok(&#effective_name as &dyn #root::JsonPointee);
1484 }
1485 Err(#ty_err)?
1486 }
1487 });
1488 }
1489 VariantTag::External => {
1490 let ty_err = if cfg!(feature = "did-you-mean") {
1492 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1493 } else {
1494 quote!(#root::JsonPointerTypeError::new(&#pointer))
1495 };
1496 tokens.append_all(quote! {
1497 #pattern => Err(#ty_err)?
1498 });
1499 }
1500 VariantTag::Adjacent { tag: tag_field, .. } => {
1501 let key = Ident::new("key", Span::mixed_site());
1504 let effective_name = ty.info().effective_name();
1505 let key_err = if cfg!(feature = "did-you-mean") {
1506 quote!(#root::JsonPointerKeyError::with_suggestions(
1507 #key,
1508 #ty,
1509 [#tag_field],
1510 ))
1511 } else {
1512 quote!(#root::JsonPointerKeyError::new(#key))
1513 };
1514 tokens.append_all(quote! {
1515 #pattern => {
1516 let Some(#key) = #pointer.head() else {
1517 return Ok(self as &dyn #root::JsonPointee);
1518 };
1519 match &*#key.to_str() {
1520 #tag_field => {
1521 return Ok(&#effective_name as &dyn #root::JsonPointee);
1522 }
1523 _ => {
1524 return Err(#key_err)?;
1525 }
1526 }
1527 }
1528 });
1529 }
1530 VariantTag::Untagged => {
1531 let ty_err = if cfg!(feature = "did-you-mean") {
1533 quote!(#root::JsonPointerTypeError::with_ty(&#pointer, #ty))
1534 } else {
1535 quote!(#root::JsonPointerTypeError::new(&#pointer))
1536 };
1537 tokens.append_all(quote! {
1538 #pattern => Err(#ty_err)?
1539 });
1540 }
1541 }
1542 }
1543}
1544
1545#[derive(Clone, Copy, Debug)]
1546enum VariantTag<'a> {
1547 Internal(&'a str),
1548 External,
1549 Adjacent { tag: &'a str, content: &'a str },
1550 Untagged,
1551}
1552
1553#[derive(Clone)]
1554enum ContainerAttr {
1555 Crate(syn::Path),
1556 RenameAll(RenameAll),
1557 Tag(String),
1558 Content(String),
1559 Untagged,
1560}
1561
1562impl ContainerAttr {
1563 fn parse_all(attrs: &[Attribute]) -> syn::Result<Vec<Self>> {
1564 attrs
1565 .iter()
1566 .map(ContainerAttr::parse_one)
1567 .flatten_ok()
1568 .try_collect()
1569 }
1570
1571 fn parse_one(attr: &Attribute) -> syn::Result<Vec<Self>> {
1572 if !attr.path().is_ident("ploidy") {
1573 return Ok(vec![]);
1574 }
1575 let mut attrs = vec![];
1576 attr.parse_nested_meta(|meta| {
1577 if meta.path.is_ident("pointer") {
1578 meta.parse_nested_meta(|meta| {
1579 if meta.path.is_ident("crate") {
1580 let value = meta.value()?;
1581 let s: syn::LitStr = value.parse()?;
1582 attrs.push(Self::Crate(s.parse()?));
1583 } else if meta.path.is_ident("rename_all") {
1584 let value = meta.value()?;
1585 let s: syn::LitStr = value.parse()?;
1586 let Some(rename) = RenameAll::from_str(&s.value()) else {
1587 return Err(meta.error(DeriveError::BadRenameAll));
1588 };
1589 attrs.push(Self::RenameAll(rename));
1590 } else if meta.path.is_ident("tag") {
1591 let value = meta.value()?;
1592 let s: syn::LitStr = value.parse()?;
1593 attrs.push(Self::Tag(s.value()));
1594 } else if meta.path.is_ident("content") {
1595 let value = meta.value()?;
1596 let s: syn::LitStr = value.parse()?;
1597 attrs.push(Self::Content(s.value()));
1598 } else if meta.path.is_ident("untagged") {
1599 attrs.push(Self::Untagged);
1600 } else {
1601 return Err(meta.error(DeriveError::UnrecognizedPointer));
1602 }
1603 Ok(())
1604 })?;
1605 } else {
1606 return Err(meta.error(DeriveError::UnrecognizedPloidy));
1607 }
1608 Ok(())
1609 })?;
1610 Ok(attrs)
1611 }
1612}
1613
1614#[derive(Clone, Debug)]
1615enum FieldAttr {
1616 Rename(String),
1617 Flatten,
1618 Skip,
1619}
1620
1621impl FieldAttr {
1622 fn parse_one(attr: &Attribute) -> syn::Result<Vec<Self>> {
1623 if !attr.path().is_ident("ploidy") {
1624 return Ok(vec![]);
1625 }
1626 let mut attrs = vec![];
1627 attr.parse_nested_meta(|meta| {
1628 if meta.path.is_ident("pointer") {
1629 meta.parse_nested_meta(|meta| {
1630 if meta.path.is_ident("rename") {
1631 let value = meta.value()?;
1632 let s: syn::LitStr = value.parse()?;
1633 attrs.push(Self::Rename(s.value()));
1634 } else if meta.path.is_ident("flatten") {
1635 attrs.push(Self::Flatten);
1636 } else if meta.path.is_ident("skip") {
1637 attrs.push(Self::Skip);
1638 } else {
1639 return Err(meta.error(DeriveError::UnrecognizedPointer));
1640 }
1641 Ok(())
1642 })?;
1643 } else {
1644 return Err(meta.error(DeriveError::UnrecognizedPloidy));
1645 }
1646 Ok(())
1647 })?;
1648 Ok(attrs)
1649 }
1650}
1651
1652#[derive(Clone, Debug)]
1653enum VariantAttr {
1654 Skip,
1655 Rename(String),
1656}
1657
1658impl VariantAttr {
1659 fn parse_one(attr: &Attribute) -> syn::Result<Vec<Self>> {
1660 if !attr.path().is_ident("ploidy") {
1661 return Ok(vec![]);
1662 }
1663 let mut attrs = vec![];
1664 attr.parse_nested_meta(|meta| {
1665 if meta.path.is_ident("pointer") {
1666 meta.parse_nested_meta(|meta| {
1667 if meta.path.is_ident("skip") {
1668 attrs.push(Self::Skip);
1669 } else if meta.path.is_ident("rename") {
1670 let value = meta.value()?;
1671 let s: syn::LitStr = value.parse()?;
1672 attrs.push(Self::Rename(s.value()));
1673 } else {
1674 return Err(meta.error(DeriveError::UnrecognizedPointer));
1675 }
1676 Ok(())
1677 })?;
1678 } else {
1679 return Err(meta.error(DeriveError::UnrecognizedPloidy));
1680 }
1681 Ok(())
1682 })?;
1683 Ok(attrs)
1684 }
1685}
1686
1687#[derive(Clone, Copy, Debug)]
1689enum RenameAll {
1690 Lowercase,
1691 Uppercase,
1692 PascalCase,
1693 CamelCase,
1694 SnakeCase,
1695 ScreamingSnakeCase,
1696 KebabCase,
1697 ScreamingKebabCase,
1698}
1699
1700impl RenameAll {
1701 const fn all() -> &'static [Self] {
1702 &[
1703 Self::Lowercase,
1704 Self::Uppercase,
1705 Self::PascalCase,
1706 Self::CamelCase,
1707 Self::SnakeCase,
1708 Self::ScreamingSnakeCase,
1709 Self::KebabCase,
1710 Self::ScreamingKebabCase,
1711 ]
1712 }
1713
1714 fn from_str(s: &str) -> Option<Self> {
1715 Some(match s {
1716 "lowercase" => RenameAll::Lowercase,
1717 "UPPERCASE" => RenameAll::Uppercase,
1718 "PascalCase" => RenameAll::PascalCase,
1719 "camelCase" => RenameAll::CamelCase,
1720 "snake_case" => RenameAll::SnakeCase,
1721 "SCREAMING_SNAKE_CASE" => RenameAll::ScreamingSnakeCase,
1722 "kebab-case" => RenameAll::KebabCase,
1723 "SCREAMING-KEBAB-CASE" => RenameAll::ScreamingKebabCase,
1724 _ => return None,
1725 })
1726 }
1727
1728 fn apply(&self, s: &str) -> String {
1729 match self {
1730 RenameAll::Lowercase => s.to_lowercase(),
1731 RenameAll::Uppercase => s.to_uppercase(),
1732 RenameAll::PascalCase => s.to_pascal_case(),
1733 RenameAll::CamelCase => s.to_lower_camel_case(),
1734 RenameAll::SnakeCase => s.to_snake_case(),
1735 RenameAll::ScreamingSnakeCase => s.to_shouty_snake_case(),
1736 RenameAll::KebabCase => s.to_kebab_case(),
1737 RenameAll::ScreamingKebabCase => s.to_shouty_kebab_case(),
1738 }
1739 }
1740}
1741
1742impl Display for RenameAll {
1743 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1744 f.write_str(match self {
1745 Self::Lowercase => "lowercase",
1746 Self::Uppercase => "UPPERCASE",
1747 Self::PascalCase => "PascalCase",
1748 Self::CamelCase => "camelCase",
1749 Self::SnakeCase => "snake_case",
1750 Self::ScreamingSnakeCase => "SCREAMING_SNAKE_CASE",
1751 Self::KebabCase => "kebab-case",
1752 Self::ScreamingKebabCase => "SCREAMING-KEBAB-CASE",
1753 })
1754 }
1755}
1756
1757#[derive(Debug, thiserror::Error)]
1758enum DeriveError {
1759 #[error("`JsonPointee` can't be derived for unions")]
1760 Union,
1761 #[error("`rename` is only supported on struct and struct-like enum variant fields")]
1762 RenameOnNonNamed,
1763 #[error("`flatten` is only supported on struct and struct-like enum variant fields")]
1764 FlattenOnNonNamed,
1765 #[error("`flatten` and `skip` are mutually exclusive")]
1766 FlattenWithSkip,
1767 #[error("`tag` is only supported on enums")]
1768 TagOnNonEnum,
1769 #[error("`content` requires `tag`")]
1770 ContentWithoutTag,
1771 #[error("`tag` and `content` must have different field names")]
1772 SameTagAndContent,
1773 #[error("only one of: `tag`, `tag` and `content`, `untagged` allowed")]
1774 ConflictingTagAttributes,
1775 #[error("`rename_all` must be one of: {}", RenameAll::all().iter().join(","))]
1776 BadRenameAll,
1777 #[error("unrecognized `#[ploidy(...)]` attribute")]
1778 UnrecognizedPloidy,
1779 #[error("unrecognized `#[ploidy(pointer(...))]` attribute")]
1780 UnrecognizedPointer,
1781}