warg_cli/commands/
download.rs

1use super::CommonOptions;
2use anyhow::Result;
3use clap::Args;
4use dialoguer::{theme::ColorfulTheme, Confirm};
5use std::path::PathBuf;
6use warg_client::ClientError;
7use warg_protocol::{registry::PackageName, VersionReq};
8
9/// Download a warg registry package.
10#[derive(Args)]
11#[clap(disable_version_flag = true)]
12pub struct DownloadCommand {
13    /// The common command options.
14    #[clap(flatten)]
15    pub common: CommonOptions,
16    /// The package name to download.
17    #[clap(value_name = "PACKAGE")]
18    pub name: PackageName,
19    /// The version requirement of the package to download; defaults to `*`.
20    #[clap(long, short, value_name = "VERSION")]
21    pub version: Option<String>,
22    /// The output path for the file. If not specified, just downloads to local cache.
23    #[clap(long, short = 'o')]
24    pub output: Option<PathBuf>,
25}
26
27impl DownloadCommand {
28    /// Executes the command.
29    pub async fn exec(self) -> Result<()> {
30        let config = self.common.read_config()?;
31        let client = self.common.create_client(&config).await?;
32
33        println!("Downloading `{name}`...", name = self.name);
34
35        // if user specifies exact verion, then set the `VersionReq` to exact match
36        let version = match &self.version {
37            Some(version) => VersionReq::parse(&format!("={}", version))?,
38            None => VersionReq::STAR,
39        };
40
41        let download = client
42            .download(&self.name, &version)
43            .await?
44            .ok_or_else(|| ClientError::PackageVersionRequirementDoesNotExist {
45                name: self.name.clone(),
46                version,
47            })?;
48
49        println!(
50            "Downloaded version: {version}\nDigest: {digest}\n",
51            version = download.version,
52            digest = download.digest
53        );
54
55        // use the `output` path specified or ask the use if wants to save in the current working
56        // directory
57        let default_file_name = format!("{name}.wasm", name = self.name.name());
58        if let Some(path) = self
59            .output
60            .map(|mut p| {
61                if p.extension()
62                    .is_some_and(|ext| ext.eq_ignore_ascii_case("wasm"))
63                {
64                    p
65                } else {
66                    p.push(&default_file_name);
67                    p
68                }
69            })
70            .or_else(|| {
71                if Confirm::with_theme(&ColorfulTheme::default())
72                    .with_prompt(format!(
73                        "Write `{default_file_name}` in current directory? y/N\n",
74                    ))
75                    .default(true)
76                    .interact()
77                    .unwrap()
78                {
79                    Some(PathBuf::from(default_file_name))
80                } else {
81                    None
82                }
83            })
84        {
85            std::fs::copy(download.path, &path)?;
86            println!(
87                "Wrote `{name}` to {path}",
88                name = self.name,
89                path = path.display(),
90            );
91        }
92
93        Ok(())
94    }
95}