Skip to main content

zoi_install/
plan.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use anyhow::Result;
5use rayon::prelude::*;
6use zoi_core::types;
7
8use crate::resolver::InstallNode;
9use crate::util;
10
11/// Details about a pre-built package available for download.
12#[derive(Clone)]
13pub struct PrebuiltDetails {
14    /// Information about the pre-built archive.
15    pub info: types::PrebuiltInfo,
16    /// The size of the archive to be downloaded, in bytes.
17    pub download_size: u64,
18    /// The estimated size of the package once installed, in bytes.
19    pub installed_size: u64
20}
21
22/// Represents the action to be taken for installing a package.
23#[derive(Clone)]
24pub enum InstallAction {
25    /// Download the pre-built archive and install it.
26    DownloadAndInstall(PrebuiltDetails),
27    /// Install from a local archive file.
28    InstallFromArchive(PathBuf),
29    /// Build the package from source and install it.
30    BuildAndInstall
31}
32
33/// Creates an execution plan for installing the resolved dependency graph.
34///
35/// This function decides the Install Action for each package:
36/// - Download and Install: If a pre-built archive exists in the registry for
37///   the target platform and the user didn't force a build.
38/// - Build and Install: If no pre-built archive is available, or if the user
39///   explicitly requested a build (via `--build` or `--type source`).
40///
41/// It utilizes `rayon` for parallel evaluation of pre-built availability across
42/// mirrors and registries.
43///
44/// # Errors
45///
46/// Returns an error if the plan cannot be created.
47pub fn create_install_plan<S: std::hash::BuildHasher + Sync>(
48    graph: &HashMap<String, InstallNode, S>,
49    build_type: Option<&str>,
50    build: bool
51) -> Result<HashMap<String, InstallAction>> {
52    let plan: HashMap<String, InstallAction> = graph
53        .par_iter()
54        .map(|(id, node)| {
55            let is_archive = std::path::Path::new(&node.source)
56                .extension()
57                .is_some_and(|ext| {
58                    ext.eq_ignore_ascii_case("zpa")
59                        || ext.eq_ignore_ascii_case("zsa")
60                });
61
62            if (build
63                || (build_type.is_some()
64                    && build_type != Some("pre-compiled")
65                    && build_type != Some("pre-built")))
66                && !is_archive
67            {
68                return (id.clone(), InstallAction::BuildAndInstall);
69            }
70
71            if is_archive {
72                return (
73                    id.clone(),
74                    InstallAction::InstallFromArchive(PathBuf::from(
75                        &node.source
76                    ))
77                );
78            }
79
80            let action = match util::find_prebuilt_info(node) {
81                Ok(Some(info)) => {
82                    let (down_size, inst_size) = util::get_package_sizes(
83                        &node.pkg,
84                        &node.registry_handle,
85                        &node.version
86                    );
87
88                    InstallAction::DownloadAndInstall(PrebuiltDetails {
89                        info,
90                        download_size: down_size,
91                        installed_size: inst_size
92                    })
93                }
94                Ok(None) => InstallAction::BuildAndInstall,
95                Err(e) => {
96                    eprintln!(
97                        "Error finding prebuilt info for {}: {}. Assuming \
98                         build.",
99                        node.pkg.name, e
100                    );
101                    InstallAction::BuildAndInstall
102                }
103            };
104            (id.clone(), action)
105        })
106        .collect();
107
108    Ok(plan)
109}