scyllax_macros_core/
enum.rs1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::ItemEnum;
4
5pub fn expand_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
6 quote! {
7 #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize, scyllax::prelude::IntEnum)]
8 #input
9 }
10}
11
12pub fn expand(input: TokenStream) -> TokenStream {
62 let input: ItemEnum = match syn::parse2(input.clone()) {
63 Ok(it) => it,
64 Err(e) => return e.to_compile_error(),
65 };
66 let ident = &input.ident;
67
68 for variant in input.variants.iter() {
69 match &variant.discriminant {
70 Some(_) => (),
71 None => {
72 return syn::Error::new_spanned(
73 variant.into_token_stream(),
74 "Enum variants must have an explicit discriminant, for example `Custom = 0`",
75 )
76 .into_compile_error();
77 }
78 }
79 }
80
81 let try_from_fields = input.variants.iter().map(|variant| {
82 let variant_ident = &variant.ident;
83 let variant_value = match &variant.discriminant {
84 Some((_, expr)) => expr,
85 None => unreachable!(),
86 };
87 quote! {
88 #variant_value => Ok(#ident::#variant_ident),
89 }
90 });
91
92 let try_from = quote! {
93 impl std::convert::TryFrom<i32> for #ident {
94 type Error = Box<dyn std::error::Error>;
95
96 #[doc = "Converts integer values to enum values"]
97 fn try_from(value: i32) -> Result<Self, Self::Error> {
98 match value {
99 #(#try_from_fields)*
100 _ => Err(anyhow::anyhow!("Invalid #ident").into()),
101 }
102 }
103 }
104 };
105
106 let to_int_fields = input.variants.iter().map(|variant| {
107 let variant_ident = &variant.ident;
108 let variant_value = match &variant.discriminant {
109 Some((_, expr)) => expr,
110 None => unreachable!(),
111 };
112 quote! {
113 Self::#variant_ident => #variant_value,
114 }
115 });
116
117 let to_int = quote! {
118 impl #ident {
119 #[doc = "Converts enum values to integer value"]
120 fn to_int(&self) -> i32 {
121 match self {
122 #(#to_int_fields)*
123 }
124 }
125 }
126 };
127
128 let scylla = quote! {
129 impl scylla::cql_to_rust::FromCqlVal<scylla::frame::response::result::CqlValue> for #ident {
130 fn from_cql(
131 cql_val: scylla::frame::response::result::CqlValue,
132 ) -> Result<Self, scylla::cql_to_rust::FromCqlValError> {
133 let data = <i32 as scylla::cql_to_rust::FromCqlVal<
134 scylla::frame::response::result::CqlValue,
135 >>::from_cql(cql_val)?;
136
137 #ident::try_from(data).map_err(|_| scylla::cql_to_rust::FromCqlValError::BadVal)
138 }
139 }
140
141 impl scylla::frame::value::Value for #ident {
142 fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), scylla::frame::value::ValueTooBig> {
143 <i32 as scylla::frame::value::Value>::serialize(&self.to_int(), buf)
144 }
145 }
146
147 impl scylla::serialize::value::SerializeCql for #ident {
148 fn serialize<'b>(
149 &self,
150 typ: &scylla::frame::response::result::ColumnType,
151 writer: scylla::serialize::CellWriter<'b>,
152 ) -> Result<
153 scylla::serialize::writers::WrittenCellProof<'b>,
154 scylla::serialize::SerializationError,
155 > {
156 <i32 as scylla::serialize::value::SerializeCql>::serialize(&self.to_int(), typ, writer)
157 }
158 }
159 };
160
161 quote! {
162 #try_from
163
164 #to_int
165
166 #scylla
167 }
168}