use std::fmt;
use std::path::PathBuf;
use rusk_wallet::{Error, RuesHttpClient};
use serde::Deserialize;
use tracing::Level;
use url::Url;
use zeroize::Zeroize;
use crate::config::Network;
use crate::io::WalletArgs;
#[derive(clap::ValueEnum, Debug, Clone)]
pub(crate) enum LogFormat {
Json,
Plain,
Coloured,
}
#[derive(clap::ValueEnum, Debug, Clone)]
pub(crate) enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
#[derive(Debug)]
pub(crate) struct Logging {
pub level: LogLevel,
pub format: LogFormat,
}
#[allow(dead_code)]
#[derive(Debug)]
pub(crate) struct Settings {
pub(crate) state: Url,
pub(crate) prover: Url,
pub(crate) archiver: Url,
pub(crate) explorer: Option<Url>,
pub(crate) logging: Logging,
pub(crate) wallet_dir: PathBuf,
pub(crate) password: Option<String>,
}
pub(crate) struct SettingsBuilder {
wallet_dir: PathBuf,
pub(crate) args: WalletArgs,
}
impl SettingsBuilder {
pub fn wallet_dir(&self) -> &PathBuf {
&self.wallet_dir
}
pub fn network(self, network: Network) -> Result<Settings, Error> {
let mut args = self.args;
let network = match (args.network, network.clone().network) {
(Some(label), Some(mut networks)) => {
let r = networks.remove(&label);
if r.is_none() {
args.password.zeroize();
return Err(Error::BadAddress);
}
r
}
(Some(_), None) => {
args.password.zeroize();
return Err(Error::BadAddress);
}
(_, _) => None,
}
.unwrap_or(network);
let state = args
.state
.as_ref()
.and_then(|value| Url::parse(value).ok())
.unwrap_or(network.state);
let prover = args
.prover
.as_ref()
.and_then(|value| Url::parse(value).ok())
.unwrap_or(network.prover);
let archiver = args
.archiver
.as_ref()
.and_then(|value| Url::parse(value).ok())
.unwrap_or_else(|| network.archiver.unwrap_or(state.clone()));
let explorer = network.explorer;
let wallet_dir =
args.wallet_dir.as_ref().cloned().unwrap_or(self.wallet_dir);
let password = args.password;
let logging = Logging {
level: args.log_level,
format: args.log_type,
};
Ok(Settings {
state,
prover,
archiver,
explorer,
logging,
wallet_dir,
password,
})
}
}
impl Settings {
pub fn args(args: WalletArgs) -> Result<SettingsBuilder, Error> {
let wallet_dir = if let Some(path) = &args.wallet_dir {
path.clone()
} else if let Some(path) = get_wallet_dir_config_from_exe_dir() {
path
} else {
let mut path = dirs::home_dir().ok_or(Error::OsNotSupported)?;
path.push(".dusk");
path.push(env!("CARGO_BIN_NAME"));
path
};
Ok(SettingsBuilder { wallet_dir, args })
}
pub async fn check_state_con(&self) -> Result<(), Error> {
RuesHttpClient::new(self.state.as_ref())?
.check_connection()
.await
.map_err(Error::from)
}
pub async fn check_prover_con(&self) -> Result<(), Error> {
RuesHttpClient::new(self.prover.as_ref())?
.check_connection()
.await
.map_err(Error::from)
}
pub async fn check_archiver_con(&self) -> Result<(), Error> {
RuesHttpClient::new(self.archiver.as_ref())?
.check_connection()
.await
.map_err(Error::from)
}
}
#[derive(Deserialize, Debug)]
struct WalletDirConfig {
wallet_dir: PathBuf,
}
fn get_wallet_dir_config_from_exe_dir() -> Option<PathBuf> {
let mut path = std::env::current_exe().ok()?;
path.pop();
let config_path = path.join("config.toml");
if !config_path.exists() {
return None;
}
let contents = std::fs::read_to_string(config_path).ok()?;
let wallet_dir_config: WalletDirConfig = toml::from_str(&contents).ok()?;
Some(wallet_dir_config.wallet_dir)
}
impl From<&LogLevel> for Level {
fn from(level: &LogLevel) -> Level {
match level {
LogLevel::Trace => Level::TRACE,
LogLevel::Debug => Level::DEBUG,
LogLevel::Info => Level::INFO,
LogLevel::Warn => Level::WARN,
LogLevel::Error => Level::ERROR,
}
}
}
impl fmt::Display for LogFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Json => "json",
Self::Plain => "plain",
Self::Coloured => "coloured",
}
)
}
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Trace => "trace",
Self::Debug => "debug",
Self::Info => "info",
Self::Warn => "warn",
Self::Error => "error",
}
)
}
}
impl fmt::Display for Logging {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Logging: [{}] ({})", self.level, self.format)
}
}
impl fmt::Display for Settings {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let separator = "─".repeat(14);
writeln!(f, "{separator}")?;
writeln!(f, "Settings")?;
writeln!(f, "{separator}")?;
writeln!(f, "Wallet directory: {}", self.wallet_dir.display())?;
writeln!(
f,
"Password: {}",
if self.password.is_some() {
"[Set]"
} else {
"[Not set]"
}
)?;
writeln!(f, "{}", separator)?;
writeln!(f, "state: {}", self.state)?;
writeln!(f, "prover: {}", self.prover)?;
if let Some(explorer) = &self.explorer {
writeln!(f, "explorer: {explorer}")?;
}
writeln!(f, "{separator}")?;
writeln!(f, "{}", self.logging)
}
}