spl_program_error_derive/
lib.rs

1//! Crate defining a procedural macro for building Solana program errors
2
3// Required to include `#[allow(clippy::integer_arithmetic)]`
4// below since the tokens generated by `quote!` in the implementation
5// for `MacroType::PrintProgramError` and `MacroType::SplProgramError`
6// trigger the lint upstream through `quote_token_with_context` within the
7// `quote` crate
8//
9// Culprit is `macro_impl.rs:66`
10#![allow(clippy::arithmetic_side_effects)]
11#![deny(missing_docs)]
12#![cfg_attr(not(test), forbid(unsafe_code))]
13
14extern crate proc_macro;
15
16mod macro_impl;
17mod parser;
18
19use {
20    crate::parser::SplProgramErrorArgs,
21    macro_impl::MacroType,
22    proc_macro::TokenStream,
23    syn::{parse_macro_input, ItemEnum},
24};
25
26/// Derive macro to add `Into<solana_program_error::ProgramError>`
27/// trait
28#[proc_macro_derive(IntoProgramError)]
29pub fn into_program_error(input: TokenStream) -> TokenStream {
30    let ItemEnum { ident, .. } = parse_macro_input!(input as ItemEnum);
31    MacroType::IntoProgramError { ident }
32        .generate_tokens()
33        .into()
34}
35
36/// Derive macro to add `solana_decode_error::DecodeError` trait
37#[proc_macro_derive(DecodeError)]
38pub fn decode_error(input: TokenStream) -> TokenStream {
39    let ItemEnum { ident, .. } = parse_macro_input!(input as ItemEnum);
40    MacroType::DecodeError { ident }.generate_tokens().into()
41}
42
43/// Derive macro to add `solana_program_error::PrintProgramError` trait
44#[proc_macro_derive(PrintProgramError)]
45pub fn print_program_error(input: TokenStream) -> TokenStream {
46    let ItemEnum {
47        ident, variants, ..
48    } = parse_macro_input!(input as ItemEnum);
49    MacroType::PrintProgramError { ident, variants }
50        .generate_tokens()
51        .into()
52}
53
54/// Proc macro attribute to turn your enum into a Solana Program Error
55///
56/// Adds:
57/// - `Clone`
58/// - `Debug`
59/// - `Eq`
60/// - `PartialEq`
61/// - `thiserror::Error`
62/// - `num_derive::FromPrimitive`
63/// - `Into<solana_program_error::ProgramError>`
64/// - `solana_decode_error::DecodeError`
65/// - `solana_program_error::PrintProgramError`
66///
67/// Optionally, you can add `hash_error_code_start: u32` argument to create
68/// a unique `u32` _starting_ error codes from the names of the enum variants.
69/// Notes:
70/// - The _error_ variant will start at this value, and the rest will be
71///   incremented by one
72/// - The value provided is only for code readability, the actual error code
73///   will be a hash of the input string and is checked against your input
74///
75/// Syntax: `#[spl_program_error(hash_error_code_start = 1275525928)]`
76/// Hash Input: `spl_program_error:<enum name>:<variant name>`
77/// Value: `u32::from_le_bytes(<hash of input>[13..17])`
78#[proc_macro_attribute]
79pub fn spl_program_error(attr: TokenStream, input: TokenStream) -> TokenStream {
80    let args = parse_macro_input!(attr as SplProgramErrorArgs);
81    let item_enum = parse_macro_input!(input as ItemEnum);
82    MacroType::SplProgramError { args, item_enum }
83        .generate_tokens()
84        .into()
85}