1use syn::{
2 parse::Parse, punctuated::Punctuated, token::Comma, Attribute, Data, DeriveInput, Expr,
3 ExprLit, Ident, ItemEnum, Lit, LitStr, Variant,
4};
5
6fn parse_attribute(attributes: &[Attribute]) -> Option<String> {
7 attributes.iter().find_map(|attr| {
8 if !attr.path().is_ident("msg") {
9 return None;
10 }
11
12 let lit: LitStr = attr.parse_args().ok()?;
13 Some(lit.value())
14 })
15}
16
17pub struct ErrorVariant {
18 pub discriminant: u32,
19 pub name: Ident,
20 pub msg: String,
21}
22
23pub struct Errors {
24 pub name: Ident,
25 pub variants: Vec<ErrorVariant>,
26}
27
28impl TryFrom<&ItemEnum> for Errors {
29 type Error = syn::Error;
30
31 fn try_from(value: &ItemEnum) -> Result<Self, Self::Error> {
32 Ok(Errors {
33 name: value.ident.clone(),
34 variants: parse_variants(&value.variants)?,
35 })
36 }
37}
38
39impl Parse for Errors {
40 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
41 let derive_input: DeriveInput = input.parse()?;
42
43 let Data::Enum(data) = &derive_input.data else {
44 return Err(syn::Error::new_spanned(
45 &derive_input,
46 "TyphoonError can only be used on enums",
47 ));
48 };
49
50 Ok(Errors {
51 name: derive_input.ident,
52 variants: parse_variants(&data.variants)?,
53 })
54 }
55}
56
57fn parse_variants(data_variants: &Punctuated<Variant, Comma>) -> syn::Result<Vec<ErrorVariant>> {
58 let mut variants = Vec::with_capacity(data_variants.len());
59 let mut latest_dis: isize = -1;
60
61 for variant in data_variants {
62 let variant_name = &variant.ident;
63 let msg = parse_attribute(&variant.attrs)
64 .ok_or(syn::Error::new_spanned(variant, "No error msg set."))?;
65
66 if let Some((_, ref expr)) = variant.discriminant {
67 if let Expr::Lit(ExprLit {
68 lit: Lit::Int(val), ..
69 }) = expr
70 {
71 latest_dis = val.base10_parse::<isize>()?
72 } else {
73 return Err(syn::Error::new_spanned(expr, "Invalid discriminant."));
74 }
75 } else {
76 latest_dis += 1;
77 }
78
79 variants.push(ErrorVariant {
80 name: variant_name.to_owned(),
81 msg,
82 discriminant: latest_dis as u32,
83 });
84 }
85
86 Ok(variants)
87}