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 import: SolanaProgram,
}
pub struct SolanaProgram {
import: Ident,
explicit: bool,
}
impl quote::ToTokens for SolanaProgram {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.import.to_tokens(tokens);
}
}
impl SolanaProgram {
pub fn wrap(&self, output: TokenStream) -> TokenStream {
if self.explicit {
output
} else {
anon_const_trick(output)
}
}
}
impl Default for SolanaProgram {
fn default() -> Self {
Self {
import: Ident::new("_clone_solana_program", 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 import = None;
while !input.is_empty() {
match SplProgramErrorArgParser::parse(input)? {
SplProgramErrorArgParser::HashErrorCodes { value, .. } => {
hash_error_code_start = Some(value.base10_parse::<u32>()?);
}
SplProgramErrorArgParser::SolanaProgramCrate { value, .. } => {
import = Some(SolanaProgram {
import: value.parse()?,
explicit: true,
});
}
}
}
Ok(Self {
hash_error_code_start,
import: import.unwrap_or(SolanaProgram::default()),
})
}
}
enum SplProgramErrorArgParser {
HashErrorCodes {
_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 = 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,
})
}
"clone_solana_program" => {
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 'clone_solana_program'"))
}
}
}
}
fn anon_const_trick(exp: TokenStream) -> TokenStream {
quote! {
const _: () = {
extern crate clone_solana_program as _clone_solana_program;
#exp
};
}
}