use inquire::validator::{ErrorMessage, Validation};
use inquire::{required, Confirm, Text};
use rust_decimal::Decimal;
use solana_cli_config::Config;
use solana_cli_output::OutputFormat;
use solana_program::pubkey::Pubkey;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::signature::{Signer, SignerError};
use std::fs::File;
use std::str::FromStr;
use std::time::Duration;
pub struct Parse<'a> {
pub text: Text<'a>,
}
pub trait Parser {
fn to_pubkey(self) -> Result<Pubkey, Box<dyn std::error::Error>>;
fn to_u128(self) -> Result<u128, Box<dyn std::error::Error>>;
fn to_u64(self) -> Result<u64, Box<dyn std::error::Error>>;
fn to_u16(self) -> Result<u16, Box<dyn std::error::Error>>;
fn to_f64(self) -> Result<f64, Box<dyn std::error::Error>>;
fn to_file(self) -> Result<String, Box<dyn std::error::Error>>;
fn to_string(self) -> Result<String, Box<dyn std::error::Error>>;
fn to_decimal(self) -> Result<Decimal, Box<dyn std::error::Error>>;
fn confirm() -> Result<(), Box<dyn std::error::Error>>;
}
impl Parser for Text<'_> {
fn to_pubkey(self) -> Result<Pubkey, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match Pubkey::from_str(s).is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong address".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(Pubkey::from_str(s.as_str()).unwrap())
}
fn to_u128(self) -> Result<u128, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match s.parse::<u128>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u128".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u128>().unwrap())
}
fn to_u64(self) -> Result<u64, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match s.parse::<u64>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u64".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u64>().unwrap())
}
fn to_u16(self) -> Result<u16, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match s.parse::<u16>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u16".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u16>().unwrap())
}
fn to_f64(self) -> Result<f64, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match s.parse::<f64>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with f64".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<f64>().unwrap())
}
fn to_file(self) -> Result<String, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match File::open(s).is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong file path".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s)
}
fn to_string(self) -> Result<String, Box<dyn std::error::Error>> {
Ok(self.prompt()?)
}
fn to_decimal(self) -> Result<Decimal, Box<dyn std::error::Error>> {
let s = self
.with_validator(|s: &str| match Decimal::from_str(s).is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong decimal".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(Decimal::from_str(s.as_str()).unwrap())
}
fn confirm() -> Result<(), Box<dyn std::error::Error>> {
let res = Confirm::new("Confirm this operation?")
.with_default(false)
.prompt();
match res {
Ok(true) => Ok(()),
Ok(false) => Err(Box::<dyn std::error::Error>::from("Cancel the operation")),
Err(_) => Err(Box::new(res.err().unwrap())),
}
}
}
impl Parse<'_> {
pub fn new(title: &str, require: bool) -> Parse {
let mut t = Text::new(title);
if require {
t = t.with_validator(required!("This field is required"));
}
Parse { text: t }
}
pub fn new_with_default<'a>(title: &'a str, message: &'a str) -> Parse<'a> {
Parse {
text: Text::new(title).with_default(message),
}
}
pub fn to_pubkey(self) -> Result<Pubkey, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match Pubkey::from_str(s).is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong address".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(Pubkey::from_str(s.as_str()).unwrap())
}
pub fn to_u128(self) -> Result<u128, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match s.parse::<u128>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u128".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u128>().unwrap())
}
pub fn to_u64(self) -> Result<u64, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match s.parse::<u64>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u64".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u64>().unwrap())
}
pub fn to_u16(self) -> Result<u16, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match s.parse::<u16>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with u16".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<u16>().unwrap())
}
pub fn to_f64(self) -> Result<f64, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match s.parse::<f64>().is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong number with f64".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s.parse::<f64>().unwrap())
}
pub fn to_file(self) -> Result<String, Box<dyn std::error::Error>> {
let s = self
.text
.with_validator(|s: &str| match File::open(s).is_err() {
true => Ok(Validation::Invalid(ErrorMessage::Custom(
"wrong file path".to_string(),
))),
false => Ok(Validation::Valid),
})
.prompt()?;
Ok(s)
}
pub fn to_string(self) -> Result<String, Box<dyn std::error::Error>> {
Ok(self.text.prompt()?)
}
pub fn confirm() -> Result<(), Box<dyn std::error::Error>> {
let res = Confirm::new("Confirm this operation?")
.with_default(false)
.prompt();
match res {
Ok(true) => Ok(()),
Ok(false) => Err(Box::<dyn std::error::Error>::from("Cancel the operation")),
Err(_) => Err(Box::new(res.err().unwrap())),
}
}
}
pub struct CliConfig {
pub signers: Vec<Box<dyn Signer>>,
pub json_rpc_url: String,
pub rpc_timeout: Duration,
pub commitment: CommitmentConfig,
pub confirm_transaction_initial_timeout: Duration,
pub output_format: OutputFormat,
pub config: Config,
}
impl CliConfig {
pub fn pubkey(&self) -> Result<Pubkey, SignerError> {
if !self.signers.is_empty() {
self.signers[0].try_pubkey()
} else {
Err(SignerError::Custom(
"Default keypair must be set if pubkey arg not provided".to_string(),
))
}
}
}
pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
pub fn process_result_from_str(msg: &str) -> ProcessResult {
Err(Box::<dyn std::error::Error>::from(msg))
}