scyllax_macros_core/
enum.rs

1use 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
12/// Enum for source type
13// #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14// pub enum EmoteProvider {
15//     Custom = 0,
16//     BTTV = 1,
17//     SevenTV = 2,
18// }
19
20// impl TryFrom<i32> for EmoteProvider {
21//     type Error = Box<dyn std::error::Error>;
22
23//     fn try_from(value: i32) -> Result<Self, Self::Error> {
24//         match value {
25//             0 => Ok(EmoteProvider::Custom),
26//             1 => Ok(EmoteProvider::BTTV),
27//             2 => Ok(EmoteProvider::SevenTV),
28//             _ => Err(anyhow::anyhow!("Invalid EmoteProvider").into()),
29//         }
30//     }
31// }
32
33// impl EmoteProvider {
34//     fn to_int(&self) -> i32 {
35//         match self {
36//             Self::Custom => 0,
37//             Self::BTTV => 1,
38//             Self::SevenTV => 2,
39//         }
40//     }
41// }
42
43// impl scylla::cql_to_rust::FromCqlVal<scylla::frame::response::result::CqlValue> for EmoteProvider {
44//     fn from_cql(
45//         cql_val: scylla::frame::response::result::CqlValue,
46//     ) -> Result<Self, scylla::cql_to_rust::FromCqlValError> {
47//         let data = <i32 as scylla::cql_to_rust::FromCqlVal<
48//             scylla::frame::response::result::CqlValue,
49//         >>::from_cql(cql_val)?;
50
51//         EmoteProvider::try_from(data).map_err(|_| scylla::cql_to_rust::FromCqlValError::BadVal)
52//     }
53// }
54
55// impl scylla::frame::value::Value for EmoteProvider {
56//     fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), scylla::frame::value::ValueTooBig> {
57//         <i32 as scylla::frame::value::Value>::serialize(&self.to_int(), buf)
58//     }
59// }
60
61pub 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}