pacsea/install/direct.rs
1//! Direct install/remove operations using integrated processes (bypassing preflight).
2
3use crate::state::{AppState, PackageItem, modal::CascadeMode};
4
5/// What: Start integrated install process for a single package (bypassing preflight).
6///
7/// Inputs:
8/// - `app`: Mutable application state
9/// - `item`: Package to install
10/// - `dry_run`: Whether to run in dry-run mode
11///
12/// Output:
13/// - If passwordless sudo is available: Proceeds directly to `PreflightExec` modal.
14/// - If passwordless sudo is not available: Transitions to `PasswordPrompt` modal.
15///
16/// Details:
17/// - Checks for passwordless sudo first to skip password prompt if available.
18/// - Both official packages (sudo pacman) and AUR packages (paru/yay need sudo for final step)
19/// require sudo, but password may not be needed if passwordless sudo is configured.
20/// - Uses `ExecutorRequest::Install` for execution.
21pub fn start_integrated_install(app: &mut AppState, item: &PackageItem, dry_run: bool) {
22 use crate::events::start_execution;
23 use crate::state::modal::PreflightHeaderChips;
24
25 app.dry_run = dry_run;
26 let items = vec![item.clone()];
27 let header_chips = PreflightHeaderChips::default();
28
29 // Check faillock status before proceeding
30 let username = std::env::var("USER").unwrap_or_else(|_| "user".to_string());
31 if let Some(lockout_msg) = crate::logic::faillock::get_lockout_message_if_locked(&username, app)
32 {
33 // User is locked out - show warning
34 app.modal = crate::state::Modal::Alert {
35 message: lockout_msg,
36 };
37 return;
38 }
39
40 // Check passwordless sudo availability (requires setting enabled AND system configured)
41 let settings = crate::theme::settings();
42 if crate::logic::password::should_use_passwordless_sudo(&settings) {
43 // Passwordless sudo enabled and available - skip password prompt and proceed directly
44 start_execution(
45 app,
46 &items,
47 crate::state::PreflightAction::Install,
48 header_chips,
49 None, // No password needed
50 );
51 } else {
52 // Passwordless sudo not enabled or not available - show password prompt
53 app.modal = crate::state::Modal::PasswordPrompt {
54 purpose: crate::state::modal::PasswordPurpose::Install,
55 items,
56 input: String::new(),
57 cursor: 0,
58 error: None,
59 };
60 app.pending_exec_header_chips = Some(header_chips);
61 }
62}
63
64/// What: Start integrated install process for multiple packages (bypassing preflight).
65///
66/// Inputs:
67/// - `app`: Mutable application state
68/// - `items`: Packages to install
69/// - `dry_run`: Whether to run in dry-run mode
70///
71/// Output:
72/// - If passwordless sudo is available: Proceeds directly to `PreflightExec` modal.
73/// - If passwordless sudo is not available: Transitions to `PasswordPrompt` modal.
74///
75/// Details:
76/// - Checks for passwordless sudo first to skip password prompt if available.
77/// - Both official packages (sudo pacman) and AUR packages (paru/yay need sudo for final step)
78/// require sudo, but password may not be needed if passwordless sudo is configured.
79/// - Uses `ExecutorRequest::Install` for execution.
80pub fn start_integrated_install_all(app: &mut AppState, items: &[PackageItem], dry_run: bool) {
81 use crate::events::start_execution;
82 use crate::state::modal::PreflightHeaderChips;
83
84 app.dry_run = dry_run;
85 let items_vec = items.to_vec();
86 let header_chips = PreflightHeaderChips::default();
87
88 // Check faillock status before proceeding
89 let username = std::env::var("USER").unwrap_or_else(|_| "user".to_string());
90 if let Some(lockout_msg) = crate::logic::faillock::get_lockout_message_if_locked(&username, app)
91 {
92 // User is locked out - show warning
93 app.modal = crate::state::Modal::Alert {
94 message: lockout_msg,
95 };
96 return;
97 }
98
99 // Check passwordless sudo availability (requires setting enabled AND system configured)
100 let settings = crate::theme::settings();
101 if crate::logic::password::should_use_passwordless_sudo(&settings) {
102 // Passwordless sudo enabled and available - skip password prompt and proceed directly
103 start_execution(
104 app,
105 &items_vec,
106 crate::state::PreflightAction::Install,
107 header_chips,
108 None, // No password needed
109 );
110 } else {
111 // Passwordless sudo not enabled or not available - show password prompt
112 app.modal = crate::state::Modal::PasswordPrompt {
113 purpose: crate::state::modal::PasswordPurpose::Install,
114 items: items_vec,
115 input: String::new(),
116 cursor: 0,
117 error: None,
118 };
119 app.pending_exec_header_chips = Some(header_chips);
120 }
121}
122
123/// What: Start integrated remove process (bypassing preflight).
124///
125/// Inputs:
126/// - `app`: Mutable application state
127/// - `names`: Package names to remove
128/// - `dry_run`: Whether to run in dry-run mode
129/// - `cascade_mode`: Cascade removal mode
130///
131/// Output:
132/// - Transitions to `PasswordPrompt` (remove always needs sudo)
133///
134/// Details:
135/// - Remove operations always need sudo, so always show `PasswordPrompt`.
136/// - Remove is intentionally never passwordless (even when `use_passwordless_sudo` is true)
137/// for safety; only install/update can skip the password prompt.
138/// - Uses `ExecutorRequest::Remove` for execution.
139pub fn start_integrated_remove_all(
140 app: &mut AppState,
141 names: &[String],
142 dry_run: bool,
143 cascade_mode: CascadeMode,
144) {
145 use crate::state::modal::PreflightHeaderChips;
146
147 app.dry_run = dry_run;
148 app.remove_cascade_mode = cascade_mode;
149
150 // Convert names to PackageItem for password prompt (we only need names, so create minimal items)
151 let items: Vec<PackageItem> = names
152 .iter()
153 .map(|name| PackageItem {
154 name: name.clone(),
155 version: String::new(),
156 description: String::new(),
157 source: crate::state::Source::Official {
158 repo: String::new(),
159 arch: String::new(),
160 },
161 popularity: None,
162 out_of_date: None,
163 orphaned: false,
164 })
165 .collect();
166
167 // Remove operations always need sudo (pacman -R requires sudo regardless of package source).
168 // Remove is intentionally never passwordless for safety (see docstring).
169 // Check faillock status before showing password prompt
170 let username = std::env::var("USER").unwrap_or_else(|_| "user".to_string());
171 if let Some(lockout_msg) = crate::logic::faillock::get_lockout_message_if_locked(&username, app)
172 {
173 // User is locked out - show warning and don't show password prompt
174 app.modal = crate::state::Modal::Alert {
175 message: lockout_msg,
176 };
177 return;
178 }
179 // Always show password prompt - user can press Enter if passwordless sudo is configured
180 app.modal = crate::state::Modal::PasswordPrompt {
181 purpose: crate::state::modal::PasswordPurpose::Remove,
182 items,
183 input: String::new(),
184 cursor: 0,
185 error: None,
186 };
187 app.pending_exec_header_chips = Some(PreflightHeaderChips::default());
188}