alloy_sol_macro_expander/expand/
enum.rs
1use super::ExpCtxt;
4use alloy_sol_macro_input::{derives_mapped, mk_doc, ContainsSolAttrs};
5use ast::{ItemEnum, Spanned};
6use proc_macro2::TokenStream;
7use quote::quote;
8use syn::Result;
9
10pub(super) fn expand(cx: &ExpCtxt<'_>, enumm: &ItemEnum) -> Result<TokenStream> {
23 let ItemEnum { name, variants, .. } = enumm;
24
25 let (sol_attrs, mut attrs) = enumm.split_attrs()?;
26 cx.derives(&mut attrs, [], false);
27 let docs = sol_attrs.docs.or(cx.attrs.docs).unwrap_or(true);
28
29 let name_s = name.to_string();
30
31 let count = variants.len();
32 if count == 0 {
33 return Err(syn::Error::new(enumm.span(), "enum has no variants"));
34 }
35 if count > 256 {
36 return Err(syn::Error::new(enumm.span(), "enum has too many variants"));
37 }
38 let max = (count - 1) as u8;
39
40 let has_invalid_variant = max != u8::MAX;
41 let invalid_variant = has_invalid_variant.then(|| {
42 let comma = (!variants.trailing_punct()).then(syn::token::Comma::default);
43
44 let has_serde = derives_mapped(&attrs).any(|path| {
45 let Some(last) = path.segments.last() else {
46 return false;
47 };
48 last.ident == "Serialize" || last.ident == "Deserialize"
49 });
50 let serde_other = has_serde.then(|| quote!(#[serde(other)]));
51
52 quote! {
53 #comma
54 #[doc(hidden)]
58 #serde_other
59 __Invalid = u8::MAX,
60 }
61 });
62 let detokenize_unwrap = if has_invalid_variant {
63 quote! { unwrap_or(Self::__Invalid) }
64 } else {
65 quote! { expect("unreachable") }
66 };
67
68 let alloy_sol_types = &cx.crates.sol_types;
69
70 let uint8 = quote!(alloy_sol_types::sol_data::Uint<8>);
71 let uint8_st = quote!(<#uint8 as alloy_sol_types::SolType>);
72
73 let index_to_variant = variants.iter().enumerate().map(|(idx, variant)| {
74 let ident = &variant.ident;
75 let idx = idx as u8;
76 quote! { #idx => ::core::result::Result::Ok(Self::#ident), }
77 });
78
79 let doc = docs.then(|| mk_doc(format!("```solidity\n{enumm}\n```")));
80 let tokens = quote! {
81 #(#attrs)*
82 #doc
83 #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields, clippy::style)]
84 #[derive(Clone, Copy)]
85 #[repr(u8)]
86 pub enum #name {
87 #variants
88 #invalid_variant
89 }
90
91 #[allow(non_camel_case_types, non_snake_case, clippy::pub_underscore_fields, clippy::style)]
92 const _: () = {
93 use #alloy_sol_types as alloy_sol_types;
94
95 #[automatically_derived]
96 impl ::core::convert::From<#name> for u8 {
97 #[inline]
98 fn from(v: #name) -> Self {
99 v as u8
100 }
101 }
102
103 #[automatically_derived]
104 impl ::core::convert::TryFrom<u8> for #name {
105 type Error = alloy_sol_types::Error;
106
107 #[inline]
108 fn try_from(value: u8) -> alloy_sol_types::Result<Self> {
109 match value {
110 #(#index_to_variant)*
111 value => ::core::result::Result::Err(alloy_sol_types::Error::InvalidEnumValue {
112 name: #name_s,
113 value,
114 max: #max,
115 })
116 }
117 }
118 }
119
120 #[automatically_derived]
121 impl alloy_sol_types::SolValue for #name {
122 type SolType = Self;
123 }
124
125 #[automatically_derived]
126 impl alloy_sol_types::private::SolTypeValue<#name> for #name {
127 #[inline]
128 fn stv_to_tokens(&self) -> #uint8_st::Token<'_> {
129 alloy_sol_types::Word::with_last_byte(*self as u8).into()
130 }
131
132 #[inline]
133 fn stv_eip712_data_word(&self) -> alloy_sol_types::Word {
134 #uint8_st::eip712_data_word(&(*self as u8))
135 }
136
137 #[inline]
138 fn stv_abi_encode_packed_to(&self, out: &mut alloy_sol_types::private::Vec<u8>) {
139 out.push(*self as u8);
140 }
141 }
142
143 #[automatically_derived]
144 impl alloy_sol_types::SolType for #name {
145 type RustType = #name;
146 type Token<'a> = #uint8_st::Token<'a>;
147
148 const SOL_NAME: &'static str = #uint8_st::SOL_NAME;
149 const ENCODED_SIZE: ::core::option::Option<usize> = #uint8_st::ENCODED_SIZE;
150 const PACKED_ENCODED_SIZE: ::core::option::Option<usize> = #uint8_st::PACKED_ENCODED_SIZE;
151
152 #[inline]
153 fn valid_token(token: &Self::Token<'_>) -> bool {
154 Self::type_check(token).is_ok()
155 }
156
157 #[inline]
158 fn type_check(token: &Self::Token<'_>) -> alloy_sol_types::Result<()> {
159 #uint8_st::type_check(token)?;
160 <Self as ::core::convert::TryFrom<u8>>::try_from(
161 #uint8_st::detokenize(*token)
162 ).map(::core::mem::drop)
163 }
164
165 #[inline]
166 fn detokenize(token: Self::Token<'_>) -> Self::RustType {
167 <Self as ::core::convert::TryFrom<u8>>::try_from(
168 #uint8_st::detokenize(token)
169 ).#detokenize_unwrap
170 }
171 }
172
173 #[automatically_derived]
174 impl alloy_sol_types::EventTopic for #name {
175 #[inline]
176 fn topic_preimage_length(rust: &Self::RustType) -> usize {
177 <#uint8 as alloy_sol_types::EventTopic>::topic_preimage_length(&(*rust as u8))
178 }
179
180 #[inline]
181 fn encode_topic_preimage(rust: &Self::RustType, out: &mut alloy_sol_types::private::Vec<u8>) {
182 <#uint8 as alloy_sol_types::EventTopic>::encode_topic_preimage(&(*rust as u8), out);
183 }
184
185 #[inline]
186 fn encode_topic(rust: &Self::RustType) -> alloy_sol_types::abi::token::WordToken {
187 <#uint8 as alloy_sol_types::EventTopic>::encode_topic(&(*rust as u8))
188 }
189 }
190
191 #[automatically_derived]
192 impl alloy_sol_types::SolEnum for #name {
193 const COUNT: usize = #count;
194 }
195 };
196 };
197 Ok(tokens)
198}