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    )?;
34
35    if graph.nodes.is_empty() {
36        return Err(anyhow!("Could not resolve package '{}'", package_source));
37    }
38
39    // Find the direct package node
40    let node = graph
41        .nodes
42        .values()
43        .find(|n| matches!(n.reason, zoi_core::types::InstallReason::Direct))
44        .ok_or_else(|| anyhow!("Could not find target package in resolution graph"))?;
45
46    println!(
47        "{} Resolved to {} v{}",
48        "::".bold().green(),
49        node.pkg.name.cyan(),
50        node.version.yellow()
51    );
52
53    let info = if download_type == DownloadType::Source {
54        util::find_source_bundle_info(node)?.ok_or_else(|| {
55            anyhow!("No source bundle (.zsa) information found for this package in the registry.")
56        })?
57    } else {
58        util::find_prebuilt_info(node)?.ok_or_else(|| {
59            anyhow!(
60                "No pre-built archive (.zpa) information found for this package in the registry."
61            )
62        })?
63    };
64
65    let filename = info
66        .final_url
67        .split('/')
68        .next_back()
69        .unwrap_or("package.archive");
70
71    let dest_path = if let Some(dir) = output_dir {
72        std::fs::create_dir_all(&dir)?;
73        dir.join(filename)
74    } else {
75        let cache_root = cache::get_archive_cache_root()?;
76        std::fs::create_dir_all(&cache_root)?;
77        cache_root.join(filename)
78    };
79
80    println!(
81        "{} Downloading to: {}",
82        "::".bold().blue(),
83        dest_path.display()
84    );
85
86    let (down_size, _) = util::get_package_sizes(&node.pkg, &node.registry_handle, &node.version);
87
88    util::download_file_with_progress(&info.final_url, &dest_path, None, Some(down_size))?;
89
90    println!(
91        "{} Successfully downloaded: {}",
92        "::".bold().green(),
93        dest_path.display()
94    );
95
96    Ok(())
97}