Skip to main content

rialo_sol_attribute_error/
lib.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4#[allow(unused_extern_crates)]
5extern crate proc_macro;
6
7use proc_macro::TokenStream;
8use quote::quote;
9use rialo_sol_syn::{
10    codegen,
11    parser::error::{self as error_parser, ErrorInput},
12    ErrorArgs,
13};
14use syn::{parse_macro_input, Expr};
15
16/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
17/// used as return types from Anchor instruction handlers. Importantly, the
18/// attribute implements
19/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the
20/// `ErrorCode` to support converting from the user defined error enum *into*
21/// the generated `Error`.
22///
23/// # Example
24///
25/// ```ignore
26/// use rialo_sol_lang::prelude::*;
27///
28/// #[program]
29/// mod errors {
30///     use super::*;
31///     pub fn hello(_ctx: Context<Hello>) -> Result<()> {
32///         Err(error!(MyError::Hello))
33///     }
34/// }
35///
36/// #[derive(Accounts)]
37/// pub struct Hello {}
38///
39/// #[error_code]
40/// pub enum MyError {
41///     #[msg("This is an error message clients will automatically display")]
42///     Hello,
43/// }
44/// ```
45///
46/// Note that we generate a new `Error` type so that we can return either the
47/// user defined error enum *or* a
48/// [`ProgramError`](../rialo_s_program/enum.ProgramError.html), which is used
49/// pervasively, throughout solana program crates. The generated `Error` type
50/// should almost never be used directly, as the user defined error is
51/// preferred. In the example above, `error!(MyError::Hello)`.
52///
53/// # Msg
54///
55/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that
56/// parsers  and IDLs can map error codes to error messages.
57#[proc_macro_attribute]
58pub fn error_code(
59    args: proc_macro::TokenStream,
60    input: proc_macro::TokenStream,
61) -> proc_macro::TokenStream {
62    let args = match args.is_empty() {
63        true => None,
64        false => Some(parse_macro_input!(args as ErrorArgs)),
65    };
66    let mut error_enum = parse_macro_input!(input as syn::ItemEnum);
67    let error = codegen::error::generate(error_parser::parse(&mut error_enum, args));
68    proc_macro::TokenStream::from(error)
69}
70
71/// Generates an [`Error::SolError`](../../rialo_sol_lang/error/enum.Error.html) that includes file and line information.
72///
73/// # Example
74/// ```rust,ignore
75/// #[program]
76/// mod errors {
77///     use super::*;
78///     pub fn example(_ctx: Context<Example>) -> Result<()> {
79///         Err(error!(MyError::Hello))
80///     }
81/// }
82///
83/// #[error_code]
84/// pub enum MyError {
85///     #[msg("This is an error message clients will automatically display")]
86///     Hello,
87/// }
88/// ```
89#[proc_macro]
90pub fn error(ts: proc_macro::TokenStream) -> TokenStream {
91    let input = parse_macro_input!(ts as ErrorInput);
92    let error_code = input.error_code;
93    create_error(error_code, true, None)
94}
95
96fn create_error(error_code: Expr, source: bool, account_name: Option<Expr>) -> TokenStream {
97    let error_origin = match (source, account_name) {
98        (false, None) => quote! { None },
99        (false, Some(account_name)) => quote! {
100            Some(rialo_sol_lang::error::ErrorOrigin::AccountName(#account_name.to_string()))
101        },
102        (true, _) => quote! {
103            Some(rialo_sol_lang::error::ErrorOrigin::Source(rialo_sol_lang::error::Source {
104                filename: file!(),
105                line: line!()
106            }))
107        },
108    };
109
110    TokenStream::from(quote! {
111        rialo_sol_lang::error::Error::from(
112            rialo_sol_lang::error::SolError {
113                error_name: #error_code.name(),
114                error_code_number: #error_code.into(),
115                error_msg: #error_code.to_string(),
116                error_origin: #error_origin,
117                compared_values: None
118            }
119        )
120    })
121}