use anyhow::{Result, anyhow};
use clap::{Parser, ValueEnum, builder::PossibleValue};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt, fs, path::PathBuf};
use url::Url;
const CONFIG_PATH: &str = ".config/vara/config.toml";
#[derive(Clone, Debug, Parser)]
pub struct Config {
#[clap(subcommand)]
pub action: Action,
}
impl Config {
pub fn exec(&self) -> Result<()> {
match &self.action {
Action::Set(s) => {
s.write(None)?;
println!("{s}");
}
Action::Get { url: _ } => {
let settings = ConfigSettings::read(None)?;
println!("{settings}");
}
}
Ok(())
}
}
#[derive(Clone, Debug, Parser, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct ConfigSettings {
#[clap(short, long, name = "URL_OR_MONIKER")]
pub url: Network,
}
impl ConfigSettings {
fn config() -> Result<PathBuf> {
dirs::home_dir()
.map(|h| h.join(CONFIG_PATH))
.ok_or_else(|| anyhow!("Could not find config.toml from ${{HOME}}/{CONFIG_PATH}"))
}
pub fn read(path: Option<PathBuf>) -> Result<ConfigSettings> {
let conf = path.unwrap_or(Self::config()?);
toml::from_str(&fs::read_to_string(conf)?).map_err(Into::into)
}
pub fn write(&self, path: Option<PathBuf>) -> Result<()> {
let conf = path.unwrap_or(Self::config()?);
if let Some(parent) = conf.parent()
&& !parent.exists()
{
fs::create_dir_all(parent)?;
}
fs::write(conf, toml::to_string_pretty(self)?).map_err(Into::into)
}
}
impl fmt::Display for ConfigSettings {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", "RPC URL".bold(), self.url.clone().as_ref())
}
}
#[derive(Clone, Debug, Parser, PartialEq, Eq)]
pub enum Action {
Set(ConfigSettings),
Get {
#[clap(short, long)]
url: bool,
},
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Network {
#[default]
Mainnet,
Testnet,
Localhost,
Custom(Url),
}
impl AsRef<str> for Network {
fn as_ref(&self) -> &str {
match self {
Self::Mainnet => "wss://rpc.vara.network:443",
Self::Testnet => "wss://testnet.vara.network:443",
Self::Localhost => "ws://localhost:9944",
Self::Custom(url) => url.as_str(),
}
}
}
impl fmt::Display for Network {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl ValueEnum for Network {
fn value_variants<'a>() -> &'a [Self] {
&[Network::Mainnet, Network::Testnet, Network::Localhost]
}
fn to_possible_value(&self) -> Option<PossibleValue> {
match self {
Self::Mainnet => Some(PossibleValue::new("mainnet")),
Self::Testnet => Some(PossibleValue::new("testnet")),
Self::Localhost => Some(PossibleValue::new("localhost")),
Self::Custom(_) => None,
}
}
fn from_str(input: &str, ignore_case: bool) -> Result<Self, String> {
let input = if ignore_case {
Cow::Owned(input.to_lowercase())
} else {
Cow::Borrowed(input)
};
Ok(match input.as_ref() {
"mainnet" => Self::Mainnet,
"testnet" => Self::Testnet,
"localhost" => Self::Localhost,
input => Self::Custom(Url::parse(input).map_err(|_| input.to_string())?),
})
}
}