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}