Skip to main content

anchor_syn/codegen/
error.rs

1use {crate::Error, quote::quote};
2
3pub fn generate(error: Error) -> proc_macro2::TokenStream {
4    let error_enum = &error.raw_enum;
5    let enum_name = &error.ident;
6    // Each arm of the `match` statement for implementing `std::fmt::Display`
7    // on the user defined error code.
8    let display_variant_dispatch: Vec<proc_macro2::TokenStream> = error
9        .raw_enum
10        .variants
11        .iter()
12        .enumerate()
13        .map(|(idx, variant)| {
14            let ident = &variant.ident;
15            #[allow(
16                clippy::indexing_slicing,
17                reason = "idx from enumerate() over variants; codes built from same variants so \
18                          indices align"
19            )]
20            let error_code = &error.codes[idx];
21            let display_msg = match &error_code.msg {
22                None => {
23                    quote! {
24                        <Self as ::std::fmt::Debug>::fmt(self, fmt)
25                    }
26                }
27                Some(msg) => {
28                    quote! {
29                        write!(fmt, #msg)
30                    }
31                }
32            };
33            quote! {
34                #enum_name::#ident => #display_msg
35            }
36        })
37        .collect();
38
39    // Each arm of the `match` statement for implementing the `name` function
40    // on the user defined error code.
41    let name_variant_dispatch: Vec<proc_macro2::TokenStream> = error
42        .raw_enum
43        .variants
44        .iter()
45        .map(|variant| {
46            let ident = &variant.ident;
47            let ident_name = ident.to_string();
48            quote! {
49                #enum_name::#ident => #ident_name.to_string()
50            }
51        })
52        .collect();
53
54    let offset = match &error.args {
55        None => quote! { anchor_lang::error::ERROR_CODE_OFFSET},
56        Some(args) => {
57            let offset = &args.offset;
58            quote! { #offset }
59        }
60    };
61
62    let ret = quote! {
63        #[derive(::std::fmt::Debug, Clone, Copy)]
64        #[repr(u32)]
65        #error_enum
66
67        impl #enum_name {
68            /// Gets the name of this [#enum_name].
69            pub fn name(&self) -> String {
70                match self {
71                    #(#name_variant_dispatch),*
72                }
73            }
74        }
75
76        impl From<#enum_name> for u32 {
77            fn from(e: #enum_name) -> u32 {
78                e as u32 + #offset
79            }
80        }
81
82        impl From<#enum_name> for anchor_lang::error::Error {
83            fn from(error_code: #enum_name) -> anchor_lang::error::Error {
84                anchor_lang::error::Error::from(
85                    anchor_lang::error::AnchorError {
86                        error_name: error_code.name(),
87                        error_code_number: error_code.into(),
88                        error_msg: error_code.to_string(),
89                        error_origin: None,
90                        compared_values: None
91                    }
92                )
93            }
94        }
95
96        impl ::std::fmt::Display for #enum_name {
97            fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> {
98                match self {
99                    #(#display_variant_dispatch),*
100                }
101            }
102        }
103    };
104
105    #[cfg(feature = "idl-build")]
106    {
107        let idl_print = crate::idl::gen_idl_print_fn_error(&error);
108        return quote! {
109            #ret
110            #idl_print
111        };
112    };
113
114    #[allow(unreachable_code)]
115    ret
116}