derive_more_impl/
try_from.rs1use proc_macro2::{Literal, TokenStream};
4use quote::{format_ident, quote, ToTokens};
5use syn::spanned::Spanned as _;
6
7use crate::utils::{
8 attr::{self, ParseMultiple as _},
9 Spanning,
10};
11
12pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStream> {
14 match &input.data {
15 syn::Data::Struct(data) => Err(syn::Error::new(
16 data.struct_token.span(),
17 "`TryFrom` cannot be derived for structs",
18 )),
19 syn::Data::Enum(data) => Ok(Expansion {
20 repr: attr::ReprInt::parse_attrs(&input.attrs, &format_ident!("repr"))?
21 .map(Spanning::into_inner)
22 .unwrap_or_default(),
23 attr: ItemAttribute::parse_attrs(&input.attrs, &format_ident!("try_from"))?
24 .map(|attr| {
25 if matches!(attr.item, ItemAttribute::Types(_)) {
26 Err(syn::Error::new(
27 attr.span,
28 "`#[try_from(repr(...))]` attribute is not supported yet",
29 ))
30 } else {
31 Ok(attr.item)
32 }
33 })
34 .transpose()?,
35 ident: input.ident.clone(),
36 generics: input.generics.clone(),
37 variants: data.variants.clone().into_iter().collect(),
38 }
39 .into_token_stream()),
40 syn::Data::Union(data) => Err(syn::Error::new(
41 data.union_token.span(),
42 "`TryFrom` cannot be derived for unions",
43 )),
44 }
45}
46
47type ItemAttribute = attr::ReprConversion;
54
55struct Expansion {
57 repr: attr::ReprInt,
59
60 attr: Option<ItemAttribute>,
62
63 ident: syn::Ident,
67
68 generics: syn::Generics,
70
71 variants: Vec<syn::Variant>,
73}
74
75impl ToTokens for Expansion {
76 fn to_tokens(&self, tokens: &mut TokenStream) {
78 if self.attr.is_none() {
79 return;
80 }
81
82 let ident = &self.ident;
83 let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
84
85 let repr_ty = &self.repr.ty();
86
87 let mut last_discriminant = quote! { 0 };
88 let mut inc = 0usize;
89 let (consts, (discriminants, variants)): (
90 Vec<syn::Ident>,
91 (Vec<TokenStream>, Vec<TokenStream>),
92 ) = self
93 .variants
94 .iter()
95 .filter_map(
96 |syn::Variant {
97 ident,
98 fields,
99 discriminant,
100 ..
101 }| {
102 if let Some(d) = discriminant {
103 last_discriminant = d.1.to_token_stream();
104 inc = 0;
105 }
106 let ret = {
107 let inc = Literal::usize_unsuffixed(inc);
108 fields.is_empty().then_some((
109 format_ident!("__DISCRIMINANT_{ident}"),
110 (
111 quote! { #last_discriminant + #inc },
112 quote! { #ident #fields },
113 ),
114 ))
115 };
116 inc += 1;
117 ret
118 },
119 )
120 .unzip();
121
122 let error = quote! { derive_more::TryFromReprError<#repr_ty> };
123
124 quote! {
125 #[automatically_derived]
126 impl #impl_generics derive_more::core::convert::TryFrom<#repr_ty #ty_generics>
127 for #ident #where_clause {
128 type Error = #error;
129
130 #[allow(non_upper_case_globals)]
131 #[inline]
132 fn try_from(val: #repr_ty) -> derive_more::core::result::Result<Self, #error> {
133 #( const #consts: #repr_ty = #discriminants; )*
134 match val {
135 #(#consts => derive_more::core::result::Result::Ok(#ident::#variants),)*
136 _ => derive_more::core::result::Result::Err(
137 derive_more::TryFromReprError::new(val)
138 ),
139 }
140 }
141 }
142 }.to_tokens(tokens);
143 }
144}