Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

program-error: Add option to specify solana_program crate #7112

Merged
merged 4 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions libraries/program-error/derive/src/macro_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {
sha2::{Digest, Sha256},
syn::{
punctuated::Punctuated, token::Comma, Expr, ExprLit, Ident, ItemEnum, Lit, LitInt, LitStr,
Token, Variant,
Path, Token, Variant,
},
};

Expand Down Expand Up @@ -36,10 +36,16 @@ pub enum MacroType {
impl MacroType {
/// Generates the corresponding tokens based on variant selection
pub fn generate_tokens(&mut self) -> proc_macro2::TokenStream {
let default_solana_program_crate_path =
Ident::new("solana_program", Span::call_site()).into();
match self {
Self::IntoProgramError { ident } => into_program_error(ident),
Self::DecodeError { ident } => decode_error(ident),
Self::PrintProgramError { ident, variants } => print_program_error(ident, variants),
Self::IntoProgramError { ident } => {
into_program_error(ident, &default_solana_program_crate_path)
}
Self::DecodeError { ident } => decode_error(ident, &default_solana_program_crate_path),
Self::PrintProgramError { ident, variants } => {
print_program_error(ident, variants, &default_solana_program_crate_path)
}
Self::SplProgramError { args, item_enum } => spl_program_error(args, item_enum),
}
}
Expand All @@ -48,20 +54,20 @@ impl MacroType {
/// Builds the implementation of
/// `Into<solana_program::program_error::ProgramError>` More specifically,
/// implements `From<Self> for solana_program::program_error::ProgramError`
pub fn into_program_error(ident: &Ident) -> proc_macro2::TokenStream {
pub fn into_program_error(ident: &Ident, solana_program_crate: &Path) -> proc_macro2::TokenStream {
quote! {
impl From<#ident> for ::solana_program::program_error::ProgramError {
impl From<#ident> for #solana_program_crate::program_error::ProgramError {
fn from(e: #ident) -> Self {
::solana_program::program_error::ProgramError::Custom(e as u32)
#solana_program_crate::program_error::ProgramError::Custom(e as u32)
}
}
}
}

/// Builds the implementation of `solana_program::decode_error::DecodeError<T>`
pub fn decode_error(ident: &Ident) -> proc_macro2::TokenStream {
pub fn decode_error(ident: &Ident, solana_program_crate: &Path) -> proc_macro2::TokenStream {
quote! {
impl<T> ::solana_program::decode_error::DecodeError<T> for #ident {
impl<T> #solana_program_crate::decode_error::DecodeError<T> for #ident {
fn type_of() -> &'static str {
stringify!(#ident)
}
Expand All @@ -74,25 +80,26 @@ pub fn decode_error(ident: &Ident) -> proc_macro2::TokenStream {
pub fn print_program_error(
ident: &Ident,
variants: &Punctuated<Variant, Comma>,
solana_program_crate: &Path,
) -> proc_macro2::TokenStream {
let ppe_match_arms = variants.iter().map(|variant| {
let variant_ident = &variant.ident;
let error_msg = get_error_message(variant)
.unwrap_or_else(|| String::from("Unknown custom program error"));
quote! {
#ident::#variant_ident => {
::solana_program::msg!(#error_msg)
#solana_program_crate::msg!(#error_msg)
}
}
});
quote! {
impl ::solana_program::program_error::PrintProgramError for #ident {
impl #solana_program_crate::program_error::PrintProgramError for #ident {
fn print<E>(&self)
where
E: 'static
+ std::error::Error
+ ::solana_program::decode_error::DecodeError<E>
+ ::solana_program::program_error::PrintProgramError
+ #solana_program_crate::decode_error::DecodeError<E>
+ #solana_program_crate::program_error::PrintProgramError
+ num_traits::FromPrimitive,
{
match self {
Expand Down Expand Up @@ -128,9 +135,9 @@ pub fn spl_program_error(

let ident = &item_enum.ident;
let variants = &item_enum.variants;
let into_program_error = into_program_error(ident);
let decode_error = decode_error(ident);
let print_program_error = print_program_error(ident, variants);
let into_program_error = into_program_error(ident, &args.solana_program_crate);
let decode_error = decode_error(ident, &args.solana_program_crate);
let print_program_error = print_program_error(ident, variants, &args.solana_program_crate);

quote! {
#[repr(u32)]
Expand Down
80 changes: 53 additions & 27 deletions libraries/program-error/derive/src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Token parsing

use {
proc_macro2::Ident,
proc_macro2::{Ident, Span},
syn::{
parse::{Parse, ParseStream},
token::Comma,
LitInt, Token,
LitInt, LitStr, Path, Token,
},
};

Expand All @@ -14,51 +14,77 @@ pub struct SplProgramErrorArgs {
/// Whether to hash the error codes using `solana_program::hash`
/// or to use the default error code assigned by `num_traits`.
pub hash_error_code_start: Option<u32>,
/// Crate to use for solana_program
pub solana_program_crate: Path,
}

impl Parse for SplProgramErrorArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self {
hash_error_code_start: None,
});
}
match SplProgramErrorArgParser::parse(input)? {
SplProgramErrorArgParser::HashErrorCodes { value, .. } => Ok(Self {
hash_error_code_start: Some(value.base10_parse::<u32>()?),
}),
let default_solana_program_crate = Ident::new("solana_program", Span::call_site());
let mut hash_error_code_start = None;
let mut solana_program_crate = None;
while !input.is_empty() {
match SplProgramErrorArgParser::parse(input)? {
SplProgramErrorArgParser::HashErrorCodes { value, .. } => {
hash_error_code_start = Some(value.base10_parse::<u32>()?);
}
SplProgramErrorArgParser::SolanaProgramCrate { value, .. } => {
solana_program_crate = value.parse()?;
}
}
}
Ok(Self {
hash_error_code_start,
solana_program_crate: solana_program_crate
.unwrap_or(default_solana_program_crate)
.into(),
})
}
}

/// Parser for args to the `#[spl_program_error]` attribute
/// ie. `#[spl_program_error(hash_error_code_start = 1275525928)]`
enum SplProgramErrorArgParser {
HashErrorCodes {
_ident: Ident,
_equals_sign: Token![=],
value: LitInt,
_comma: Option<Comma>,
},
SolanaProgramCrate {
_equals_sign: Token![=],
value: LitStr,
_comma: Option<Comma>,
},
}

impl Parse for SplProgramErrorArgParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
let _ident = {
let ident = input.parse::<Ident>()?;
if ident != "hash_error_code_start" {
return Err(input.error("Expected argument 'hash_error_code_start'"));
let ident = input.parse::<Ident>()?;
match ident.to_string().as_str() {
"hash_error_code_start" => {
let _equals_sign = input.parse::<Token![=]>()?;
let value = input.parse::<LitInt>()?;
let _comma: Option<Comma> = input.parse().unwrap_or(None);
Ok(Self::HashErrorCodes {
_equals_sign,
value,
_comma,
})
}
ident
};
let _equals_sign = input.parse::<Token![=]>()?;
let value = input.parse::<LitInt>()?;
let _comma: Option<Comma> = input.parse().unwrap_or(None);
Ok(Self::HashErrorCodes {
_ident,
_equals_sign,
value,
_comma,
})
"solana_program_crate" => {
let _equals_sign = input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
let _comma: Option<Comma> = input.parse().unwrap_or(None);
Ok(Self::SolanaProgramCrate {
_equals_sign,
value,
_comma,
})
}
_ => {
Err(input
.error("Expected argument 'hash_error_code_start' or 'solana_program_crate'"))
}
}
}
}
17 changes: 17 additions & 0 deletions libraries/program-error/tests/spl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,20 @@ fn test_library_error_codes() {
first_error_as_u32 + 3,
);
}

/// Example error with solana_program crate set
#[spl_program_error(solana_program_crate = "solana_program")]
enum ExampleSolanaProgramCrateError {
/// This is a very informative error
#[error("This is a very informative error")]
VeryInformativeError,
/// This is a super important error
#[error("This is a super important error")]
SuperImportantError,
}

/// Tests that all macros compile
#[test]
fn test_macros_compile_with_solana_program_crate() {
let _ = ExampleSolanaProgramCrateError::VeryInformativeError;
}
5 changes: 4 additions & 1 deletion libraries/type-length-value/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use spl_program_error::*;

/// Errors that may be returned by the Token program.
#[spl_program_error(hash_error_code_start = 1_202_666_432)]
#[spl_program_error(
hash_error_code_start = 1_202_666_432,
solana_program_crate = "solana_program"
)]
pub enum TlvError {
/// Type not found in TLV data
#[error("Type not found in TLV data")]
Expand Down