Skip to main content

zoi_cli/cmd/
download.rs

1use anyhow::{Result, anyhow};
2use colored::*;
3use std::path::PathBuf;
4use zoi_core::cache;
5use zoi_install::resolver::resolve_dependency_graph;
6use zoi_install::util;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DownloadType {
10    Archive,
11    Source,
12}
13
14pub fn run(
15    package_source: String,
16    download_type: DownloadType,
17    output_dir: Option<PathBuf>,
18) -> Result<()> {
19    println!(
20        "{} Resolving package '{}' for download...",
21        "::".bold().blue(),
22        package_source.cyan()
23    );
24
25    let (graph, _) = resolve_dependency_graph(
26        std::slice::from_ref(&package_source),
27        None,
28        false,
29        true,
30        false,
31        None,
32        true,
33        None,
34    )?;
35
36    if graph.nodes.is_empty() {
37        return Err(anyhow!("Could not resolve package '{}'", package_source));
38    }
39
40    // Find the direct package node
41    let node = graph
42        .nodes
43        .values()
44        .find(|n| matches!(n.reason, zoi_core::types::InstallReason::Direct))
45        .ok_or_else(|| anyhow!("Could not find target package in resolution graph"))?;
46
47    println!(
48        "{} Resolved to {} v{}",
49        "::".bold().green(),
50        node.pkg.name.cyan(),
51        node.version.yellow()
52    );
53
54    let info = if download_type == DownloadType::Source {
55        util::find_source_bundle_info(node)?.ok_or_else(|| {
56            anyhow!("No source bundle (.zsa) information found for this package in the registry.")
57        })?
58    } else {
59        util::find_prebuilt_info(node)?.ok_or_else(|| {
60            anyhow!(
61                "No pre-built archive (.zpa) information found for this package in the registry."
62            )
63        })?
64    };
65
66    let filename = info
67        .final_url
68        .split('/')
69        .next_back()
70        .unwrap_or("package.archive");
71
72    let dest_path = if let Some(dir) = output_dir {
73        std::fs::create_dir_all(&dir)?;
74        dir.join(filename)
75    } else {
76        let cache_root = cache::get_archive_cache_root()?;
77        std::fs::create_dir_all(&cache_root)?;
78        cache_root.join(filename)
79    };
80
81    println!(
82        "{} Downloading to: {}",
83        "::".bold().blue(),
84        dest_path.display()
85    );
86
87    let (down_size, _) = util::get_package_sizes(&node.pkg, &node.registry_handle, &node.version);
88
89    util::download_file_with_progress(&info.final_url, &dest_path, None, Some(down_size))?;
90
91    println!(
92        "{} Successfully downloaded: {}",
93        "::".bold().green(),
94        dest_path.display()
95    );
96
97    Ok(())
98}