1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use super::CommonOptions;
use anyhow::Result;
use clap::Args;
use dialoguer::{theme::ColorfulTheme, Confirm};
use std::path::PathBuf;
use warg_client::ClientError;
use warg_protocol::{registry::PackageName, VersionReq};

/// Download a warg registry package.
#[derive(Args)]
#[clap(disable_version_flag = true)]
pub struct DownloadCommand {
    /// The common command options.
    #[clap(flatten)]
    pub common: CommonOptions,
    /// The package name to download.
    #[clap(value_name = "PACKAGE")]
    pub name: PackageName,
    /// The version requirement of the package to download; defaults to `*`.
    #[clap(long, short, value_name = "VERSION")]
    pub version: Option<String>,
    /// The output path for the file. If not specified, just downloads to local cache.
    #[clap(long, short = 'o')]
    pub output: Option<PathBuf>,
}

impl DownloadCommand {
    /// Executes the command.
    pub async fn exec(self) -> Result<()> {
        let config = self.common.read_config()?;
        let client = self.common.create_client(&config).await?;

        println!("Downloading `{name}`...", name = self.name);

        // if user specifies exact verion, then set the `VersionReq` to exact match
        let version = match &self.version {
            Some(version) => VersionReq::parse(&format!("={}", version))?,
            None => VersionReq::STAR,
        };

        let download = client
            .download(&self.name, &version)
            .await?
            .ok_or_else(|| ClientError::PackageVersionRequirementDoesNotExist {
                name: self.name.clone(),
                version,
            })?;

        println!(
            "Downloaded version: {version}\nDigest: {digest}\n",
            version = download.version,
            digest = download.digest
        );

        // use the `output` path specified or ask the use if wants to save in the current working
        // directory
        let default_file_name = format!("{name}.wasm", name = self.name.name());
        if let Some(path) = self
            .output
            .map(|mut p| {
                if p.extension()
                    .is_some_and(|ext| ext.eq_ignore_ascii_case("wasm"))
                {
                    p
                } else {
                    p.push(&default_file_name);
                    p
                }
            })
            .or_else(|| {
                if Confirm::with_theme(&ColorfulTheme::default())
                    .with_prompt(format!(
                        "Write `{default_file_name}` in current directory? y/N\n",
                    ))
                    .default(true)
                    .interact()
                    .unwrap()
                {
                    Some(PathBuf::from(default_file_name))
                } else {
                    None
                }
            })
        {
            std::fs::copy(download.path, &path)?;
            println!(
                "Wrote `{name}` to {path}",
                name = self.name,
                path = path.display(),
            );
        }

        Ok(())
    }
}