mod types;
pub mod cmd;
pub mod config;
pub mod util;
use clap::Parser;
use cmd::CliCommand;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
pub fn run() -> Result<(), anyhow::Error> {
let args = Args::parse();
initialize_logging(&args);
args.cmd.run()
}
#[derive(clap::Parser, Debug)]
#[clap(about, version)]
struct Args {
#[clap(flatten)]
pub verbosity: clap_verbosity_flag::Verbosity<clap_verbosity_flag::WarnLevel>,
#[clap(subcommand)]
cmd: cmd::SubCmd,
}
#[derive(clap::Parser, Debug, Clone)]
pub struct ApiOpts {
#[clap(long, env = "WASMER_TOKEN")]
pub token: Option<String>,
#[clap(long)]
pub registry: Option<url::Url>,
}
impl ApiOpts {
const DEV_REGISTRY: &'static str = "https://registry.wapm.dev/graphql";
fn user_token_from_config(registry: &str) -> Result<Option<String>, anyhow::Error> {
let wasmer_dir = wasmer_registry::WasmerConfig::get_wasmer_dir()
.map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?;
let config = wasmer_registry::WasmerConfig::from_file(&wasmer_dir)
.map_err(|e| anyhow::anyhow!("could not load config {e}"))?;
let token_opt = config
.registry
.tokens
.iter()
.find(|t| t.registry == registry)
.map(|x| x.token.clone());
Ok(token_opt)
}
fn token(&self) -> Result<Option<String>, anyhow::Error> {
if let Some(token) = &self.token {
Ok(Some(token.clone()))
} else {
let registry = self
.registry
.as_ref()
.map(|x| x.as_str())
.unwrap_or(Self::DEV_REGISTRY);
Self::user_token_from_config(registry)
}
}
fn client(&self) -> Result<wasmer_api::backend::BackendClient, anyhow::Error> {
let registry = self
.registry
.clone()
.unwrap_or_else(|| Self::DEV_REGISTRY.parse().unwrap());
let client = wasmer_api::backend::BackendClient::new(registry);
let client = if let Some(token) = self.token()? {
client.with_auth_token(token)
} else {
client
};
Ok(client)
}
}
#[derive(clap::Parser, Debug)]
pub struct ItemFormatOpts {
#[clap(short = 'f', long, default_value = "yaml")]
pub format: util::render::ItemFormat,
}
#[derive(clap::Parser, Debug)]
pub struct ListFormatOpts {
#[clap(short = 'f', long, default_value = "table")]
pub format: util::render::ListFormat,
}
fn initialize_logging(args: &Args) {
let level = args.verbosity.log_level_filter();
let fmt_layer = fmt::layer()
.with_target(true)
.with_span_events(fmt::format::FmtSpan::CLOSE)
.with_writer(std::io::stderr)
.compact();
let default_level = match level {
log::LevelFilter::Off => tracing::level_filters::LevelFilter::OFF,
log::LevelFilter::Error => tracing::level_filters::LevelFilter::ERROR,
log::LevelFilter::Warn => tracing::level_filters::LevelFilter::WARN,
log::LevelFilter::Info => tracing::level_filters::LevelFilter::INFO,
log::LevelFilter::Debug => tracing::level_filters::LevelFilter::DEBUG,
log::LevelFilter::Trace => tracing::level_filters::LevelFilter::TRACE,
};
let filter_layer = EnvFilter::builder()
.with_default_directive(default_level.into())
.from_env_lossy();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}