use clap::Parser;
use color_eyre::{Result, eyre::eyre};
use gclient::{
GearApi,
ext::sp_core::{self, Pair as _, crypto::Ss58Codec, sr25519::Pair},
};
use gring::Keyring;
use gsdk::Api;
use std::{env, time::Duration};
use tracing_subscriber::EnvFilter;
#[async_trait::async_trait]
pub trait App: Parser + Sync {
fn timeout(&self) -> Duration {
Api::DEFAULT_TIMEOUT
}
fn verbose(&self) -> u8 {
0
}
fn endpoint(&self) -> Option<String> {
None
}
fn passwd(&self) -> Option<String> {
None
}
fn ss58_address(&self) -> String {
gring::cmd::Command::store()
.and_then(Keyring::load)
.and_then(|mut s| s.primary())
.map(|k| k.address)
.unwrap_or(
Pair::from_string("//Alice", None)
.expect("Alice always works")
.public()
.to_ss58check(),
)
}
async fn exec(&self) -> anyhow::Result<()>;
async fn api(&self) -> anyhow::Result<GearApi> {
let endpoint = self.endpoint();
Api::builder()
.timeout(self.timeout())
.uri(endpoint.as_deref().unwrap_or(Api::DEFAULT_ENDPOINT))
.build()
.await
.map(Into::into)
.map_err(Into::into)
}
async fn signer(&self) -> anyhow::Result<GearApi> {
let passwd = self.passwd();
let api = Api::builder()
.timeout(self.timeout())
.uri(self.endpoint().as_deref().unwrap_or(Api::DEFAULT_ENDPOINT))
.build()
.await?;
let pair = Keyring::load(gring::cmd::Command::store()?)?
.primary()?
.decrypt(passwd.clone().and_then(|p| hex::decode(p).ok()).as_deref())?;
Ok(GearApi::from((api, pair.into())))
}
async fn run(&self) -> Result<()> {
color_eyre::install()?;
sp_core::crypto::set_default_ss58_version(runtime_primitives::VARA_SS58_PREFIX.into());
let name = Self::command().get_name().to_string();
let filter = if env::var(EnvFilter::DEFAULT_ENV).is_ok() {
EnvFilter::from_default_env()
} else {
match self.verbose() {
0 => format!("{name}=info,gsdk=info").into(),
1 => format!("{name}=debug,gsdk=debug").into(),
2 => "debug".into(),
_ => "trace".into(),
}
};
tracing_subscriber::fmt()
.with_env_filter(filter)
.without_time()
.try_init()
.map_err(|e| eyre!("{e}"))?;
self.exec()
.await
.map_err(|e| eyre!("Failed to run app, {e}"))
}
}