derive_enum_from_into/
lib.rs1#![doc = include_str!("../README.md")]
2
3mod from;
4mod into;
5
6use proc_macro2::Ident;
7use quote::quote;
8use syn::{Data, DataEnum, DeriveInput, Field, Fields, GenericArgument, GenericParam, TypePath};
9
10type VariantFieldEntry = (Ident, Field);
11
12#[proc_macro_derive(EnumFrom, attributes(from_ignore))]
13pub fn derive_from(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
14 let derive_input = syn::parse::<DeriveInput>(input).unwrap();
15 if let Data::Enum(r#enum) = derive_input.data {
16 from::derive_enum_from(r#enum, derive_input.ident, derive_input.generics).into()
17 } else {
18 quote!( compile_error!("Can only derive EnumFrom on enums"); ).into()
19 }
20}
21
22#[proc_macro_derive(EnumTryInto, attributes(try_into_references, try_into_ignore))]
23pub fn derive_enum_into(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
24 let derive_input = syn::parse::<DeriveInput>(input).unwrap();
25 if let Data::Enum(r#enum) = derive_input.data {
26 into::derive_enum_into(
27 r#enum,
28 derive_input.ident,
29 derive_input.attrs,
30 derive_input.generics,
31 )
32 .into()
33 } else {
34 quote!( compile_error!("Can only derive EnumTryInto on enums"); ).into()
35 }
36}
37
38pub(crate) enum Unique<T> {
39 Unique(T),
40 NonUnique,
41}
42
43fn get_unnamed_single_non_ignored_variants<'a>(
45 r#enum: &'a DataEnum,
46 ignore_ident: &'a str,
47) -> impl Iterator<Item = VariantFieldEntry> + 'a {
48 r#enum.variants.iter().filter_map(move |variant| {
49 if let Fields::Unnamed(unnamed_fields) = &variant.fields {
50 if unnamed_fields.unnamed.len() == 1
51 && !variant
52 .attrs
53 .iter()
54 .any(|attr| attr.path().is_ident(ignore_ident))
55 {
56 Some((
57 variant.ident.clone(),
58 unnamed_fields.unnamed.first().unwrap().clone(),
59 ))
60 } else {
61 None
62 }
63 } else {
64 None
65 }
66 })
67}
68
69fn type_args_from_parameters<'a>(
70 params: impl Iterator<Item = &'a GenericParam> + 'a,
71) -> impl Iterator<Item = GenericArgument> + 'a {
72 fn ident_to_ty(ident: Ident) -> TypePath {
73 TypePath {
74 path: syn::Path {
75 leading_colon: None,
76 segments: std::iter::once(syn::PathSegment {
77 ident,
78 arguments: Default::default(),
79 })
80 .collect(),
81 },
82 qself: None,
83 }
84 }
85
86 params.map(|param| match param {
87 GenericParam::Type(ty) => GenericArgument::Type(ident_to_ty(ty.ident.clone()).into()),
88 GenericParam::Lifetime(lt) => GenericArgument::Lifetime(lt.lifetime.clone()),
89 GenericParam::Const(cst) => GenericArgument::Type(ident_to_ty(cst.ident.clone()).into()),
90 })
91}
92
93#[cfg(test)]
94mod test {
95 use quote::quote;
96 use syn::{parse_quote, DataEnum, DeriveInput, Generics};
97
98 fn dissect_input(input: DeriveInput) -> (DataEnum, proc_macro2::Ident, Generics) {
99 if let syn::Data::Enum(data_enum) = input.data {
100 (data_enum, input.ident, input.generics)
101 } else {
102 unreachable!();
103 }
104 }
105
106 #[test]
107 fn duplicate_type_fields_detection() {
108 let input: DeriveInput = parse_quote! {
109 enum X {
110 A(i32),
111 B(String),
112 C(String),
113 }
114 };
115 let (enum1, name, generics) = dissect_input(input);
116
117 let result = crate::from::derive_enum_from(enum1, name, generics);
118 assert_eq!(
119 result.to_string(),
120 quote! {
121 #[automatically_derived]
122 impl ::core::convert::From<i32> for X {
123 #[inline]
124 fn from(item: i32) -> Self {
125 Self::A(item)
126 }
127 }
128 }
129 .to_string()
130 )
131 }
132
133 #[test]
134 fn lifetimes() {
135 let input: DeriveInput = parse_quote! {
136 enum X<'a> {
137 A(&'a i32),
138 }
139 };
140 let (enum1, name, generics) = dissect_input(input);
141
142 let result = crate::from::derive_enum_from(enum1, name, generics);
143 assert_eq!(
144 result.to_string(),
145 quote! {
146 #[automatically_derived]
147 impl<'a> ::core::convert::From<&'a i32> for X<'a> {
148 #[inline]
149 fn from(item: &'a i32) -> Self {
150 Self::A(item)
151 }
152 }
153 }
154 .to_string()
155 )
156 }
157}