enum_map_derive/
derive_struct.rs
1use crate::type_length;
7use proc_macro2::TokenStream;
8use quote::quote;
9use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Ident, Index};
10
11pub fn generate(name: Ident, data_struct: DataStruct) -> TokenStream {
12 StructGenerator::from_fields(&data_struct.fields).finish(&name)
13}
14
15#[derive(Debug)]
19struct StructGenerator {
20 length: TokenStream,
21 from_usize: TokenStream,
22 into_usize: TokenStream,
23}
24
25impl StructGenerator {
26 fn from_fields(fields: &Fields) -> Self {
27 match fields {
28 Fields::Unit => Self::from_unit_fields(),
29 Fields::Unnamed(fields_data) => Self::from_unnamed_fields(fields_data),
30 Fields::Named(fields_data) => Self::from_named_fields(fields_data),
31 }
32 }
33
34 fn from_unit_fields() -> Self {
35 Self {
36 length: quote! { 1usize },
37 from_usize: quote! { Self },
38 into_usize: quote! { 0usize },
39 }
40 }
41
42 fn from_unnamed_fields(fields: &FieldsUnnamed) -> Self {
43 let mut params_from = quote! {};
44 let mut into_usize = quote! { 0usize };
45 let mut length = quote! { 1usize };
46 for (i, field) in fields.unnamed.iter().enumerate() {
47 let ty = &field.ty;
48 let index_ident = Index::from(i);
49 let field_length = type_length(ty);
50
51 into_usize = quote! {
52 (#into_usize + #length * ::enum_map::Enum::into_usize(self.#index_ident))
53 };
54
55 params_from = quote! {
56 #params_from <#ty as ::enum_map::Enum>::from_usize(
57 value / #length % #field_length
58 ),
59 };
60
61 length = quote! { (#length * #field_length) };
62 }
63
64 let from_usize = quote! { Self(#params_from) };
65 Self {
66 length,
67 from_usize,
68 into_usize,
69 }
70 }
71
72 fn from_named_fields(fields: &FieldsNamed) -> Self {
73 let mut params_from = quote! {};
74 let mut into_usize = quote! { 0usize };
75 let mut length = quote! { 1usize };
76 for field in fields.named.iter() {
77 let ty = &field.ty;
78 let ident = field.ident.as_ref().unwrap();
79 let field_length = type_length(ty);
80
81 into_usize = quote! {
82 (#into_usize + #length * ::enum_map::Enum::into_usize(self.#ident))
83 };
84
85 params_from = quote! {
86 #params_from #ident: <#ty as ::enum_map::Enum>::from_usize(
87 value / #length % #field_length
88 ),
89 };
90
91 length = quote! { (#field_length * #length) };
92 }
93
94 let from_usize = quote! { Self { #params_from } };
95 Self {
96 length,
97 from_usize,
98 into_usize,
99 }
100 }
101
102 fn finish(&self, name: &Ident) -> TokenStream {
103 let length = &self.length;
104 let from_usize = &self.from_usize;
105 let into_usize = &self.into_usize;
106
107 quote! {
108 #[automatically_derived]
109 impl ::enum_map::Enum for #name {
110 const LENGTH: ::enum_map::usize = #length;
111
112 #[inline]
113 fn from_usize(value: ::enum_map::usize) -> Self {
114 #from_usize
115 }
116
117 #[inline]
118 fn into_usize(self) -> ::enum_map::usize {
119 #into_usize
120 }
121 }
122
123 #[automatically_derived]
124 impl<V> ::enum_map::EnumArray<V> for #name {
125 type Array = [V; #length];
126 }
127 }
128 }
129}