use crate::assets::{self, CustomAssetInstallOptions};
use anyhow::{Result, anyhow};
use clap::{Args, Subcommand};
use std::path::{Path, PathBuf};
#[derive(Args)]
pub(crate) struct AssetsArgs {
#[command(subcommand)]
command: AssetsCommand,
}
#[derive(Subcommand)]
enum AssetsCommand {
Add(AssetsAddArgs),
Pull(AssetsPullArgs),
List,
Path(AssetsPathArgs),
}
#[derive(Args, Clone)]
struct AssetsAddArgs {
#[arg(value_name = "PATH_OR_NAME")]
path_or_name: String,
#[arg(long, value_name = "PATH")]
casper_node: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
casper_sidecar: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
chainspec: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
node_config: Option<PathBuf>,
#[arg(long, value_name = "PATH")]
sidecar_config: Option<PathBuf>,
}
#[derive(Args, Clone)]
struct AssetsPullArgs {
#[arg(long)]
target: Option<String>,
#[arg(long)]
force: bool,
}
#[derive(Args, Clone)]
struct AssetsPathArgs {
#[arg(value_name = "ASSET_NAME")]
asset_name: String,
}
pub(crate) async fn run(args: AssetsArgs) -> Result<()> {
match args.command {
AssetsCommand::Add(add) => run_assets_add(add).await,
AssetsCommand::Pull(pull) => run_assets_pull(pull).await,
AssetsCommand::List => run_assets_list().await,
AssetsCommand::Path(path) => run_assets_path(path).await,
}
}
async fn run_assets_add(args: AssetsAddArgs) -> Result<()> {
if is_custom_asset_add_requested(&args) {
let mut missing = Vec::new();
if args.casper_node.is_none() {
missing.push("--casper-node");
}
if args.casper_sidecar.is_none() {
missing.push("--casper-sidecar");
}
if args.chainspec.is_none() {
missing.push("--chainspec");
}
if args.node_config.is_none() {
missing.push("--node-config");
}
if args.sidecar_config.is_none() {
missing.push("--sidecar-config");
}
if !missing.is_empty() {
return Err(anyhow!(
"custom asset mode requires: {}",
missing.join(", ")
));
}
let opts = CustomAssetInstallOptions {
name: args.path_or_name.clone(),
casper_node: args.casper_node.expect("checked above"),
casper_sidecar: args.casper_sidecar.expect("checked above"),
chainspec: args.chainspec.expect("checked above"),
node_config: args.node_config.expect("checked above"),
sidecar_config: args.sidecar_config.expect("checked above"),
};
assets::install_custom_asset(&opts).await?;
println!(
"custom asset '{}' installed into {}",
opts.name,
assets::custom_assets_root()?.display()
);
return Ok(());
}
let path = PathBuf::from(&args.path_or_name);
if looks_like_url(&path) {
return Err(anyhow!(
"assets URL is not supported yet; provide a local .tar.gz path"
));
}
assets::install_assets_bundle(&path).await?;
println!(
"assets installed into {}",
assets::assets_bundle_root()?.display()
);
Ok(())
}
async fn run_assets_pull(args: AssetsPullArgs) -> Result<()> {
assets::pull_assets_bundles(args.target.as_deref(), args.force).await?;
Ok(())
}
async fn run_assets_list() -> Result<()> {
let mut versions = assets::list_bundle_versions().await?;
let custom_assets = assets::list_custom_asset_names().await?;
if versions.is_empty() && custom_assets.is_empty() {
return Err(anyhow!("no assets bundles or custom assets found"));
}
versions.sort_by(|a, b| b.cmp(a));
for version in versions {
println!("{}", version);
}
for name in custom_assets {
println!("custom/{}", name);
}
Ok(())
}
async fn run_assets_path(args: AssetsPathArgs) -> Result<()> {
let path = assets::custom_asset_path(&args.asset_name).await?;
println!("{}", path.display());
Ok(())
}
fn looks_like_url(path: &Path) -> bool {
let value = path.to_string_lossy();
value.starts_with("http://") || value.starts_with("https://")
}
fn is_custom_asset_add_requested(args: &AssetsAddArgs) -> bool {
args.casper_node.is_some()
|| args.casper_sidecar.is_some()
|| args.chainspec.is_some()
|| args.node_config.is_some()
|| args.sidecar_config.is_some()
}