use std::str::FromStr;
use anyhow::{Context, Error, Result};
use clap::Parser;
use cosmrs::{tx::Fee, Denom};
use getset::Getters;
use regex::Regex;
use serde::Deserialize;
use super::coin::CoinFromStr;
#[derive(Debug, Parser, Getters, Clone, Deserialize)]
#[get = "pub"]
pub struct GasArgs {
#[clap(long)]
gas: Option<String>,
#[clap(long)]
gas_limit: Option<u64>,
}
impl TryFrom<GasArgs> for Fee {
type Error = Error;
fn try_from(value: GasArgs) -> Result<Self, Self::Error> {
Fee::try_from(&value)
.with_context(|| format!("Unable to convert GasArgs into Fee: {:?}", &value))
}
}
impl TryFrom<&GasArgs> for Fee {
type Error = Error;
fn try_from(value: &GasArgs) -> Result<Self, Self::Error> {
let error_msg = "`gas` and `gas_limit` must be specified if either of them is specified. Neglect both to estimate fee automatically.";
let amount = value
.gas
.as_ref()
.with_context(|| error_msg)?
.parse::<CoinFromStr>()?
.inner()
.to_owned();
Ok(Fee::from_amount_and_gas(
amount,
value.gas_limit.with_context(|| error_msg)?,
))
}
}
#[derive(Debug, Clone)]
pub struct GasPrice {
pub amount: f64,
pub denom: Denom,
}
impl FromStr for GasPrice {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let re = Regex::new(r"^(\d+\.?\d*)(.+)$").unwrap();
let caps = re
.captures(s)
.with_context(|| format!("Unable to parse `{s}` as Coin."))?;
let gas_price = GasPrice {
amount: caps
.get(1)
.with_context(|| format!("`{s}` does not contain valid amount"))?
.as_str()
.parse()
.unwrap(),
denom: caps
.get(2)
.with_context(|| format!("`{s}` does not contain valid denom"))?
.as_str()
.parse()
.unwrap(),
};
Ok(gas_price)
}
}
#[derive(Debug, Clone)]
pub enum Gas {
Specified(Fee),
Auto {
gas_price: GasPrice,
gas_adjustment: f64,
},
}
impl Gas {
pub fn from_args(args: &GasArgs, gas_price: &str, gas_adjustment: &f64) -> Result<Self> {
if args.gas_limit.is_none() && args.gas.is_none() {
Ok(Self::Auto {
gas_price: gas_price.parse()?,
gas_adjustment: gas_adjustment.to_owned(),
})
} else {
Ok(Self::Specified(Fee::try_from(args)?))
}
}
}