clmm-common 0.1.39

Blockchain, Clmm for Solana
Documentation
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))
}