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}