1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use crate::Error;
use quote::quote;

pub fn generate(error: Error) -> proc_macro2::TokenStream {
    let error_enum = &error.raw_enum;
    let enum_name = &error.ident;
    // Each arm of the `match` statement for implementing `std::fmt::Display`
    // on the user defined error code.
    let display_variant_dispatch: Vec<proc_macro2::TokenStream> = error
        .raw_enum
        .variants
        .iter()
        .enumerate()
        .map(|(idx, variant)| {
            let ident = &variant.ident;
            let error_code = &error.codes[idx];
            let display_msg = match &error_code.msg {
                None => {
                    quote! {
                        <Self as std::fmt::Debug>::fmt(self, fmt)
                    }
                }
                Some(msg) => {
                    quote! {
                        write!(fmt, #msg)
                    }
                }
            };
            quote! {
                #enum_name::#ident => #display_msg
            }
        })
        .collect();

    // Each arm of the `match` statement for implementing the `name` function
    // on the user defined error code.
    let name_variant_dispatch: Vec<proc_macro2::TokenStream> = error
        .raw_enum
        .variants
        .iter()
        .map(|variant| {
            let ident = &variant.ident;
            let ident_name = ident.to_string();
            quote! {
                #enum_name::#ident => #ident_name.to_string()
            }
        })
        .collect();

    let offset = match error.args {
        None => quote! { anchor_lang::error::ERROR_CODE_OFFSET},
        Some(args) => {
            let offset = &args.offset;
            quote! { #offset }
        }
    };

    quote! {
        #[derive(std::fmt::Debug, Clone, Copy)]
        #[repr(u32)]
        #error_enum

        impl #enum_name {
            /// Gets the name of this [#enum_name].
            pub fn name(&self) -> String {
                match self {
                    #(#name_variant_dispatch),*
                }
            }
        }

        impl From<#enum_name> for u32 {
            fn from(e: #enum_name) -> u32 {
                e as u32 + #offset
            }
        }

        impl From<#enum_name> for anchor_lang::error::Error {
            fn from(error_code: #enum_name) -> Error {
                anchor_lang::error::Error::from(
                    anchor_lang::error::AnchorError {
                        error_name: error_code.name(),
                        error_code_number: error_code.into(),
                        error_msg: error_code.to_string(),
                        source: None,
                        account_name: None
                    }
                )
            }
        }

        impl std::fmt::Display for #enum_name {
            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
                match self {
                    #(#display_variant_dispatch),*
                }
            }
        }
    }
}