use {
proc_macro2::{Ident, Span, TokenStream},
quote::quote,
syn::{
parse::{Parse, ParseStream},
token::Comma,
LitInt, LitStr, Token,
},
};
pub struct SplProgramErrorArgs {
pub hash_error_code_start: Option<u32>,
pub program_error_import: SolanaProgramError,
pub decode_error_import: SolanaDecodeError,
}
pub struct SolanaProgramError {
import: Ident,
explicit: bool,
}
impl quote::ToTokens for SolanaProgramError {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.import.to_tokens(tokens);
}
}
impl SolanaProgramError {
pub fn wrap(&self, output: TokenStream) -> TokenStream {
if self.explicit {
output
} else {
program_error_anon_const_trick(output)
}
}
}
impl Default for SolanaProgramError {
fn default() -> Self {
Self {
import: Ident::new("_solana_program_error", Span::call_site()),
explicit: false,
}
}
}
pub struct SolanaDecodeError {
import: Ident,
explicit: bool,
}
impl quote::ToTokens for SolanaDecodeError {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.import.to_tokens(tokens);
}
}
impl SolanaDecodeError {
pub fn wrap(&self, output: TokenStream) -> TokenStream {
if self.explicit {
output
} else {
decode_error_anon_const_trick(output)
}
}
}
impl Default for SolanaDecodeError {
fn default() -> Self {
Self {
import: Ident::new("_solana_decode_error", Span::call_site()),
explicit: false,
}
}
}
impl Parse for SplProgramErrorArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut hash_error_code_start = None;
let mut program_error_import = None;
let mut decode_error_import = None;
while !input.is_empty() {
match SplProgramErrorArgParser::parse(input)? {
SplProgramErrorArgParser::HashErrorCodes { value, .. } => {
hash_error_code_start = Some(value.base10_parse::<u32>()?);
}
SplProgramErrorArgParser::SolanaProgramErrorCrate { value, .. } => {
program_error_import = Some(SolanaProgramError {
import: value.parse()?,
explicit: true,
});
}
SplProgramErrorArgParser::SolanaDecodeErrorCrate { value, .. } => {
decode_error_import = Some(SolanaDecodeError {
import: value.parse()?,
explicit: true,
});
}
}
}
Ok(Self {
hash_error_code_start,
program_error_import: program_error_import.unwrap_or(SolanaProgramError::default()),
decode_error_import: decode_error_import.unwrap_or(SolanaDecodeError::default()),
})
}
}
enum SplProgramErrorArgParser {
HashErrorCodes {
_equals_sign: Token![=],
value: LitInt,
_comma: Option<Comma>,
},
SolanaProgramErrorCrate {
_equals_sign: Token![=],
value: LitStr,
_comma: Option<Comma>,
},
SolanaDecodeErrorCrate {
_equals_sign: Token![=],
value: LitStr,
_comma: Option<Comma>,
},
}
impl Parse for SplProgramErrorArgParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
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,
})
}
"solana_program_error" => {
let _equals_sign = input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
let _comma: Option<Comma> = input.parse().unwrap_or(None);
Ok(Self::SolanaProgramErrorCrate {
_equals_sign,
value,
_comma,
})
}
"solana_decode_error" => {
let _equals_sign = input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
let _comma: Option<Comma> = input.parse().unwrap_or(None);
Ok(Self::SolanaDecodeErrorCrate {
_equals_sign,
value,
_comma,
})
}
_ => Err(input.error("Expected argument 'hash_error_code_start', 'solana_program_error', or 'solana_decode_error'")),
}
}
}
fn program_error_anon_const_trick(exp: TokenStream) -> TokenStream {
quote! {
const _: () = {
extern crate solana_program_error as _solana_program_error;
#exp
};
}
}
fn decode_error_anon_const_trick(exp: TokenStream) -> TokenStream {
quote! {
const _: () = {
extern crate solana_decode_error as _solana_decode_error;
#exp
};
}
}