-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
lib.rs
127 lines (121 loc) · 3.88 KB
/
lib.rs
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use anchor_syn::parser::error::{self as error_parser, ErrorWithAccountNameInput};
use anchor_syn::ErrorArgs;
use anchor_syn::{codegen, parser::error::ErrorInput};
use syn::{parse_macro_input, Expr};
/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
/// used as return types from Anchor instruction handlers. Importantly, the
/// attribute implements
/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the
/// `ErrorCode` to support converting from the user defined error enum *into*
/// the generated `Error`.
///
/// # Example
///
/// ```ignore
/// use anchor_lang::prelude::*;
///
/// #[program]
/// mod errors {
/// use super::*;
/// pub fn hello(_ctx: Context<Hello>) -> Result<()> {
/// Err(error!(MyError::Hello))
/// }
/// }
///
/// #[derive(Accounts)]
/// pub struct Hello {}
///
/// #[error_code]
/// pub enum MyError {
/// #[msg("This is an error message clients will automatically display")]
/// Hello,
/// }
/// ```
///
/// Note that we generate a new `Error` type so that we can return either the
/// user defined error enum *or* a
/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used
/// pervasively, throughout solana program crates. The generated `Error` type
/// should almost never be used directly, as the user defined error is
/// preferred. In the example above, `error!(MyError::Hello)`.
///
/// # Msg
///
/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
/// parsers and IDLs can map error codes to error messages.
#[proc_macro_attribute]
pub fn error_code(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = match args.is_empty() {
true => None,
false => Some(parse_macro_input!(args as ErrorArgs)),
};
let mut error_enum = parse_macro_input!(input as syn::ItemEnum);
let error = codegen::error::generate(error_parser::parse(&mut error_enum, args));
proc_macro::TokenStream::from(error)
}
/// Generates an [`Error::AnchorError`](../../anchor_lang/error/enum.Error.html) that includes file and line information.
///
/// # Example
/// ```rust,ignore
/// #[program]
/// mod errors {
/// use super::*;
/// pub fn example(_ctx: Context<Example>) -> Result<()> {
/// Err(error!(MyError::Hello))
/// }
/// }
///
/// #[error_code]
/// pub enum MyError {
/// #[msg("This is an error message clients will automatically display")]
/// Hello,
/// }
/// ```
#[proc_macro]
pub fn error(ts: proc_macro::TokenStream) -> TokenStream {
let input = parse_macro_input!(ts as ErrorInput);
let error_code = input.error_code;
create_error(error_code, true, None)
}
#[proc_macro]
pub fn error_with_account_name(ts: proc_macro::TokenStream) -> TokenStream {
let input = parse_macro_input!(ts as ErrorWithAccountNameInput);
let error_code = input.error_code;
let account_name = input.account_name;
create_error(error_code, false, Some(account_name))
}
fn create_error(error_code: Expr, source: bool, account_name: Option<Expr>) -> TokenStream {
let source = if source {
quote! {
Some(anchor_lang::error::Source {
filename: file!(),
line: line!()
})
}
} else {
quote! {
None
}
};
let account_name = match account_name {
Some(_) => quote! { Some(#account_name.to_string()) },
None => quote! { None },
};
TokenStream::from(quote! {
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: #source,
account_name: #account_name
}
)
})
}