arch_toolkit/install/mod.rs
1//! Install module for building package installation, removal, and update commands.
2//!
3//! This module builds commands — it **never executes them**. Every builder
4//! returns a [`CommandSpec`] (program + argument vector) that callers can:
5//!
6//! - spawn directly via [`CommandSpec::to_command`] (no shell, no quoting bugs),
7//! - render for display or terminals via [`CommandSpec::to_shell_string`],
8//! - or simply print (a "dry run" is displaying the command instead of running it).
9//!
10//! Functionality:
11//!
12//! - **Command building** — pacman install/remove/update, AUR helper install
13//! - **Batch planning** — split mixed target lists between pacman and an AUR helper
14//! - **Detection** — find the preferred AUR helper (`paru` → `yay`) and privilege
15//! tool (`doas` → `sudo`) on `PATH`
16//! - **Shell safety** — strict package-name validation and POSIX quoting helpers
17//!
18//! # Features
19//!
20//! This module requires the `install` feature flag (which enables `deps` for
21//! the shared `PackageRef`/`PackageSource` types):
22//!
23//! ```toml
24//! [dependencies]
25//! arch-toolkit = { version = "0.3", features = ["install"] }
26//! ```
27//!
28//! # Examples
29//!
30//! ## Build and Display an Install Command
31//!
32//! ```
33//! use arch_toolkit::install::build_pacman_install;
34//! use arch_toolkit::types::install::InstallOptions;
35//!
36//! let spec = build_pacman_install(&["ripgrep", "fd"], &InstallOptions::default())?;
37//! // Dry run: display instead of executing
38//! println!("Would run: {}", spec.to_shell_string());
39//! # Ok::<(), arch_toolkit::error::ArchToolkitError>(())
40//! ```
41//!
42//! ## Detect Tools and Plan a Mixed Batch
43//!
44//! ```no_run
45//! use arch_toolkit::install::{build_batch_install, detect_aur_helper, detect_privilege_tool};
46//! use arch_toolkit::types::install::InstallOptions;
47//! use arch_toolkit::{PackageRef, PackageSource};
48//!
49//! let targets = vec![
50//! PackageRef::official("ripgrep", "14.0.0", "extra", "x86_64"),
51//! PackageRef::aur("yay-bin", "12.0.0"),
52//! ];
53//! let plan = build_batch_install(
54//! &targets,
55//! detect_aur_helper(),
56//! detect_privilege_tool(),
57//! &InstallOptions::default(),
58//! None::<&std::collections::HashSet<String>>,
59//! )?;
60//! for command in &plan.commands {
61//! println!("{command}");
62//! }
63//! # Ok::<(), arch_toolkit::error::ArchToolkitError>(())
64//! ```
65//!
66//! ## Remove Packages with Cascade Control
67//!
68//! ```
69//! use arch_toolkit::install::{build_remove_command, with_privilege};
70//! use arch_toolkit::types::install::{CascadeMode, PrivilegeTool};
71//!
72//! let spec = with_privilege(
73//! PrivilegeTool::Sudo,
74//! build_remove_command(&["old-package"], CascadeMode::CascadeWithConfigs, true)?,
75//! );
76//! assert_eq!(spec.to_shell_string(), "sudo pacman -Rns --noconfirm -- old-package");
77//! # Ok::<(), arch_toolkit::error::ArchToolkitError>(())
78//! ```
79//!
80//! ## Execute a Built Command (caller's decision)
81//!
82//! ```no_run
83//! use arch_toolkit::install::build_update_command;
84//!
85//! let spec = build_update_command(None, true);
86//! let status = spec.to_command().status()?;
87//! println!("Update exited with: {status}");
88//! # Ok::<(), std::io::Error>(())
89//! ```
90
91mod batch;
92mod command;
93mod detect;
94mod shell;
95
96// Re-export types from types module
97pub use crate::types::install::{
98 AurHelper, CascadeMode, CommandSpec, InstallOptions, PrivilegeTool,
99};
100
101// Re-export command builders
102pub use command::{
103 NO_AUR_HELPER_MESSAGE, aur_install_shell_fallback, aur_update_shell_fallback,
104 build_aur_install, build_aur_update_command, build_force_sync_update_command,
105 build_pacman_install, build_remove_command, build_update_command, with_privilege,
106};
107
108// Re-export batch planning
109pub use batch::{InstallPlan, build_batch_install};
110
111// Re-export detection
112pub use detect::{
113 detect_aur_helper, detect_privilege_tool, is_aur_helper_available, is_privilege_tool_available,
114};
115
116// Re-export shell utilities
117pub use shell::{
118 command_on_path, is_safe_package_name, resolve_command_on_path, shell_single_quote,
119 validate_package_names,
120};