pacsea/state/modal.rs
1//! Modal dialog state for the UI.
2
3use crate::state::types::{OptionalDepRow, PackageItem, Source};
4use std::collections::HashSet;
5
6/// What: Enumerates the high-level operations represented in the preflight
7/// workflow.
8///
9/// - Input: Selected by callers when presenting confirmation or preflight
10/// dialogs.
11/// - Output: Indicates whether the UI should prepare for an install or remove
12/// transaction.
13/// - Details: Drives copy, button labels, and logging in the preflight and
14/// execution flows.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum PreflightAction {
17 /// Install packages action.
18 Install,
19 /// Remove packages action.
20 Remove,
21 /// Downgrade packages action.
22 Downgrade,
23}
24
25/// What: Purpose for password prompt.
26///
27/// Inputs:
28/// - Set when showing password prompt modal.
29///
30/// Output:
31/// - Used to customize prompt message and context.
32///
33/// Details:
34/// - Indicates which operation requires sudo authentication.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum PasswordPurpose {
37 /// Installing packages.
38 Install,
39 /// Removing packages.
40 Remove,
41 /// Updating system.
42 Update,
43 /// Downgrading packages.
44 Downgrade,
45 /// Syncing file database.
46 FileSync,
47}
48
49/// What: Identifies which tab within the preflight modal is active.
50///
51/// - Input: Set by UI event handlers responding to user navigation.
52/// - Output: Informs the renderer which data set to display (summary, deps,
53/// files, etc.).
54/// - Details: Enables multi-step review of package operations without losing
55/// context between tabs.
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum PreflightTab {
58 /// Summary tab showing overview of package operations.
59 Summary,
60 /// Dependencies tab showing dependency analysis.
61 Deps,
62 /// Files tab showing file change analysis.
63 Files,
64 /// Services tab showing service impact analysis.
65 Services,
66 /// Sandbox tab showing sandbox analysis.
67 Sandbox,
68}
69
70/// Removal cascade strategy for `pacman` operations.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum CascadeMode {
73 /// `pacman -R` – remove targets only.
74 Basic,
75 /// `pacman -Rs` – remove targets and orphaned dependencies.
76 Cascade,
77 /// `pacman -Rns` – cascade removal and prune configuration files.
78 CascadeWithConfigs,
79}
80
81impl CascadeMode {
82 /// Return the `pacman` flag sequence corresponding to this `CascadeMode`.
83 #[must_use]
84 pub const fn flag(self) -> &'static str {
85 match self {
86 Self::Basic => "-R",
87 Self::Cascade => "-Rs",
88 Self::CascadeWithConfigs => "-Rns",
89 }
90 }
91
92 /// Short text describing the effect of this `CascadeMode`.
93 #[must_use]
94 pub const fn description(self) -> &'static str {
95 match self {
96 Self::Basic => "targets only",
97 Self::Cascade => "remove dependents",
98 Self::CascadeWithConfigs => "dependents + configs",
99 }
100 }
101
102 /// Whether this `CascadeMode` allows removal when dependents exist.
103 #[must_use]
104 pub const fn allows_dependents(self) -> bool {
105 !matches!(self, Self::Basic)
106 }
107
108 /// Cycle to the next `CascadeMode`.
109 #[must_use]
110 pub const fn next(self) -> Self {
111 match self {
112 Self::Basic => Self::Cascade,
113 Self::Cascade => Self::CascadeWithConfigs,
114 Self::CascadeWithConfigs => Self::Basic,
115 }
116 }
117}
118
119/// Dependency information for a package in the preflight dependency view.
120#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
121pub struct DependencyInfo {
122 /// Package name.
123 pub name: String,
124 /// Required version constraint (e.g., ">=1.2.3" or "1.2.3").
125 pub version: String,
126 /// Current status of this dependency.
127 pub status: DependencyStatus,
128 /// Source repository or origin.
129 pub source: DependencySource,
130 /// Packages that require this dependency.
131 pub required_by: Vec<String>,
132 /// Packages that this dependency depends on (transitive deps).
133 pub depends_on: Vec<String>,
134 /// Whether this is a core repository package.
135 pub is_core: bool,
136 /// Whether this is a critical system package.
137 pub is_system: bool,
138}
139
140/// Summary statistics for reverse dependency analysis of removal targets.
141#[derive(Clone, Debug, Default)]
142pub struct ReverseRootSummary {
143 /// Package slated for removal.
144 pub package: String,
145 /// Number of packages that directly depend on this package (depth 1).
146 pub direct_dependents: usize,
147 /// Number of packages that depend on this package through other packages (depth ≥ 2).
148 pub transitive_dependents: usize,
149 /// Total number of dependents (direct + transitive).
150 pub total_dependents: usize,
151}
152
153/// Status of a dependency relative to the current system state.
154#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
155pub enum DependencyStatus {
156 /// Already installed and version matches requirement.
157 Installed {
158 /// Installed version of the package.
159 version: String,
160 },
161 /// Not installed, needs to be installed.
162 ToInstall,
163 /// Installed but outdated, needs upgrade.
164 ToUpgrade {
165 /// Current installed version.
166 current: String,
167 /// Required version for upgrade.
168 required: String,
169 },
170 /// Conflicts with existing packages.
171 Conflict {
172 /// Reason for the conflict.
173 reason: String,
174 },
175 /// Cannot be found in configured repositories or AUR.
176 Missing,
177}
178
179/// Source of a dependency package.
180#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
181pub enum DependencySource {
182 /// Official repository package.
183 Official {
184 /// Repository name (e.g., "core", "extra").
185 repo: String,
186 },
187 /// AUR package.
188 Aur,
189 /// Local package (not in repos).
190 Local,
191}
192
193/// What: Restart preference applied to an impacted `systemd` service.
194///
195/// Inputs:
196/// - Assigned automatically from heuristics or by user toggles within the Services tab.
197///
198/// Output:
199/// - Guides post-transaction actions responsible for restarting (or deferring) service units.
200///
201/// Details:
202/// - Provides a simplified binary choice: restart immediately or defer for later manual handling.
203#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
204pub enum ServiceRestartDecision {
205 /// Explicitly restart the unit after the transaction.
206 Restart,
207 /// Defer restarting the unit.
208 Defer,
209}
210
211/// What: Aggregated information about a `systemd` unit affected by the pending operation.
212///
213/// Inputs:
214/// - Populated by the service impact resolver which correlates package file lists and
215/// `systemctl` state.
216///
217/// Output:
218/// - Supplies UI rendering with package provenance, restart status, and the current user choice.
219///
220/// Details:
221/// - `providers` lists packages that ship the unit. `is_active` flags if the unit currently runs.
222/// `needs_restart` indicates detected impact. `recommended_decision` records the resolver default,
223/// and `restart_decision` reflects any user override applied in the UI.
224#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
225pub struct ServiceImpact {
226 /// Fully-qualified unit name (e.g., `sshd.service`).
227 pub unit_name: String,
228 /// Packages contributing this unit.
229 pub providers: Vec<String>,
230 /// Whether the unit is active (`systemctl is-active == active`).
231 pub is_active: bool,
232 /// Whether a restart is recommended because files/configs will change.
233 pub needs_restart: bool,
234 /// Resolver-suggested action prior to user adjustments.
235 pub recommended_decision: ServiceRestartDecision,
236 /// Restart decision currently applied to the unit (may differ from recommendation).
237 pub restart_decision: ServiceRestartDecision,
238}
239
240/// Type of file change in a package operation.
241#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
242pub enum FileChangeType {
243 /// File will be newly installed (not currently on system).
244 New,
245 /// File exists but will be replaced/updated.
246 Changed,
247 /// File will be removed (for Remove operations).
248 Removed,
249}
250
251/// Information about a file change in a package operation.
252#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
253pub struct FileChange {
254 /// Full path of the file.
255 pub path: String,
256 /// Type of change (new/changed/removed).
257 pub change_type: FileChangeType,
258 /// Package that owns this file.
259 pub package: String,
260 /// Whether this is a configuration file (under /etc or marked as backup).
261 pub is_config: bool,
262 /// Whether this file is predicted to create a .pacnew file (config conflict).
263 pub predicted_pacnew: bool,
264 /// Whether this file is predicted to create a .pacsave file (config removal).
265 pub predicted_pacsave: bool,
266}
267
268/// File information for a package in the preflight file view.
269#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
270pub struct PackageFileInfo {
271 /// Package name.
272 pub name: String,
273 /// List of file changes for this package.
274 pub files: Vec<FileChange>,
275 /// Total number of files (including directories).
276 pub total_count: usize,
277 /// Number of new files.
278 pub new_count: usize,
279 /// Number of changed files.
280 pub changed_count: usize,
281 /// Number of removed files.
282 pub removed_count: usize,
283 /// Number of configuration files.
284 pub config_count: usize,
285 /// Number of files predicted to create .pacnew files.
286 pub pacnew_candidates: usize,
287 /// Number of files predicted to create .pacsave files.
288 pub pacsave_candidates: usize,
289}
290
291/// What: Risk severity buckets used by the preflight summary header and messaging.
292///
293/// Inputs:
294/// - Assigned by the summary resolver based on aggregate risk score thresholds.
295///
296/// Output:
297/// - Guides color selection and descriptive labels for risk indicators across the UI.
298///
299/// Details:
300/// - Defaults to `Low` so callers without computed risk can render a safe baseline.
301#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
302pub enum RiskLevel {
303 /// Low risk level.
304 Low,
305 /// Medium risk level.
306 Medium,
307 /// High risk level.
308 High,
309}
310
311impl Default for RiskLevel {
312 /// What: Provide a baseline risk level when no assessment has been computed yet.
313 ///
314 /// Inputs: None.
315 ///
316 /// Output: Always returns `RiskLevel::Low`.
317 ///
318 /// Details:
319 /// - Keeps `Default` implementations for composite structs simple while biasing towards safety.
320 fn default() -> Self {
321 Self::Low
322 }
323}
324
325/// What: Aggregated chip metrics displayed in the Preflight header, execution sidebar, and post-summary.
326///
327/// Inputs:
328/// - Populated by the summary planner once package metadata and risk scores are available.
329///
330/// Output:
331/// - Supplies counts and byte deltas for UI components needing condensed statistics.
332///
333/// Details:
334/// - Stores signed install deltas so removals show negative values without additional conversion.
335#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
336pub struct PreflightHeaderChips {
337 /// Number of packages in the operation.
338 pub package_count: usize,
339 /// Total download size in bytes.
340 pub download_bytes: u64,
341 /// Net change in installed size in bytes (positive for installs, negative for removals).
342 pub install_delta_bytes: i64,
343 /// Number of AUR packages in the operation.
344 pub aur_count: usize,
345 /// Risk score (0-255) computed from various risk factors.
346 pub risk_score: u8,
347 /// Risk level category (Low/Medium/High).
348 pub risk_level: RiskLevel,
349}
350
351impl Default for PreflightHeaderChips {
352 /// What: Provide neutral header chip values prior to summary computation.
353 ///
354 /// Inputs: None.
355 ///
356 /// Output: Returns a struct with zeroed counters and low risk classification.
357 ///
358 /// Details:
359 /// - Facilitates cheap initialization for modals created before async planners finish.
360 fn default() -> Self {
361 Self {
362 package_count: 0,
363 download_bytes: 0,
364 install_delta_bytes: 0,
365 aur_count: 0,
366 risk_score: 0,
367 risk_level: RiskLevel::Low,
368 }
369 }
370}
371
372/// What: Version comparison details for a single package in the preflight summary.
373///
374/// Inputs:
375/// - Filled with installed and target versions, plus classification flags.
376///
377/// Output:
378/// - Enables the UI to display per-package version deltas, major bumps, and downgrade warnings.
379///
380/// Details:
381/// - Notes array allows the planner to surface auxiliary hints (e.g., pacnew prediction or service impacts).
382#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
383pub struct PreflightPackageSummary {
384 /// Package name.
385 pub name: String,
386 /// Package source (official/AUR/local).
387 pub source: Source,
388 /// Installed version, if present.
389 pub installed_version: Option<String>,
390 /// Target version to be installed.
391 pub target_version: String,
392 /// Whether the operation downgrades the package.
393 pub is_downgrade: bool,
394 /// Whether the update is a major version bump.
395 pub is_major_bump: bool,
396 /// Download size contribution for this package when available.
397 pub download_bytes: Option<u64>,
398 /// Net installed size delta contributed by this package (signed).
399 pub install_delta_bytes: Option<i64>,
400 /// Notes or warnings specific to this package.
401 pub notes: Vec<String>,
402}
403
404/// What: Comprehensive dataset backing the Preflight Summary tab.
405///
406/// Inputs:
407/// - Populated by summary resolution logic once package metadata, sizes, and risk heuristics are computed.
408///
409/// Output:
410/// - Delivers structured information for tab body rendering, risk callouts, and contextual notes.
411///
412/// Details:
413/// - `summary_notes` aggregates high-impact bullet points (e.g., kernel updates, pacnew predictions).
414#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
415pub struct PreflightSummaryData {
416 /// Per-package summaries for the operation.
417 pub packages: Vec<PreflightPackageSummary>,
418 /// Total number of packages represented in `packages`.
419 pub package_count: usize,
420 /// Number of AUR-sourced packages participating in the plan.
421 pub aur_count: usize,
422 /// Total download size for the plan.
423 pub download_bytes: u64,
424 /// Net install size delta for the plan (signed).
425 pub install_delta_bytes: i64,
426 /// Aggregate risk score (0-255).
427 pub risk_score: u8,
428 /// Aggregate risk level (Low/Medium/High).
429 pub risk_level: RiskLevel,
430 /// Reasons contributing to the risk score.
431 pub risk_reasons: Vec<String>,
432 /// Packages classified as major version bumps (e.g., 1.x -> 2.0).
433 pub major_bump_packages: Vec<String>,
434 /// Core/system packages flagged as high impact (kernel, glibc, etc.).
435 pub core_system_updates: Vec<String>,
436 /// Total predicted .pacnew files across all packages.
437 pub pacnew_candidates: usize,
438 /// Total predicted .pacsave files across all packages.
439 pub pacsave_candidates: usize,
440 /// Packages with configuration merge warnings (.pacnew expected).
441 pub config_warning_packages: Vec<String>,
442 /// Services likely requiring restart after the transaction.
443 pub service_restart_units: Vec<String>,
444 /// Free-form warnings assembled by the summary planner to highlight notable risks.
445 pub summary_warnings: Vec<String>,
446 /// Notes summarizing key items in the plan.
447 pub summary_notes: Vec<String>,
448}
449
450/// What: Captures all dialog state for the various modal overlays presented in
451/// the Pacsea TUI.
452///
453/// - Input: Mutated by event handlers in response to user actions or
454/// background updates.
455/// - Output: Drives conditional rendering and behavior of each modal type.
456/// - Details: Acts as a tagged union so only one modal can be active at a time
457/// while carrying the precise data needed for that modal's UI.
458#[derive(Debug, Clone, Default)]
459#[allow(clippy::large_enum_variant)]
460pub enum Modal {
461 /// No modal is currently displayed.
462 #[default]
463 None,
464 /// Informational alert with a non-interactive message.
465 Alert {
466 /// Alert message text.
467 message: String,
468 },
469 /// Loading indicator shown during background computation.
470 Loading {
471 /// Loading message text.
472 message: String,
473 },
474 /// Confirmation dialog for installing the given items.
475 ConfirmInstall {
476 /// Package items to install.
477 items: Vec<PackageItem>,
478 },
479 /// Confirmation dialog for reinstalling already installed packages.
480 ConfirmReinstall {
481 /// Packages that are already installed (shown in the confirmation dialog).
482 items: Vec<PackageItem>,
483 /// All packages to install (including both installed and not installed).
484 all_items: Vec<PackageItem>,
485 /// Header chip metrics for the operation.
486 header_chips: PreflightHeaderChips,
487 },
488 /// Confirmation dialog for batch updates that may cause dependency conflicts.
489 ConfirmBatchUpdate {
490 /// Package items to update.
491 items: Vec<PackageItem>,
492 /// Whether this is a dry-run operation.
493 dry_run: bool,
494 },
495 /// Confirmation dialog for continuing AUR update after pacman failed.
496 ConfirmAurUpdate {
497 /// Message explaining the situation.
498 message: String,
499 },
500 /// Preflight summary before executing any action.
501 Preflight {
502 /// Packages selected for the operation.
503 items: Vec<PackageItem>,
504 /// Action to perform (install/remove/downgrade).
505 action: PreflightAction,
506 /// Currently active preflight tab.
507 tab: PreflightTab,
508 /// Aggregated summary information for versions, sizes, and risk cues.
509 summary: Option<Box<PreflightSummaryData>>,
510 /// Scroll offset (lines) for the Summary tab content (mouse scrolling only).
511 summary_scroll: u16,
512 /// Header chip data shared across summary, execution, and post-summary screens.
513 header_chips: PreflightHeaderChips,
514 /// Resolved dependency information (populated when Deps tab is accessed).
515 dependency_info: Vec<DependencyInfo>,
516 /// Selected index in the dependency list (for navigation).
517 dep_selected: usize,
518 /// Set of dependency names with expanded tree nodes (for tree view).
519 dep_tree_expanded: HashSet<String>,
520 /// Error message from dependency resolution, if any.
521 deps_error: Option<String>,
522 /// File information (populated when Files tab is accessed).
523 file_info: Vec<PackageFileInfo>,
524 /// Selected index in the file list (for navigation).
525 file_selected: usize,
526 /// Set of package names with expanded file lists (for Files tab tree view).
527 file_tree_expanded: HashSet<String>,
528 /// Error message from file resolution, if any.
529 files_error: Option<String>,
530 /// Service impact information (populated when Services tab is accessed).
531 service_info: Vec<ServiceImpact>,
532 /// Selected index in the service impact list (for navigation).
533 service_selected: usize,
534 /// Whether service impacts have been resolved for the current session.
535 services_loaded: bool,
536 /// Error message from service resolution, if any.
537 services_error: Option<String>,
538 /// Sandbox information for AUR packages (populated when Sandbox tab is accessed).
539 sandbox_info: Vec<crate::logic::sandbox::SandboxInfo>,
540 /// Selected index in the sandbox display list (for navigation - can be package or dependency).
541 sandbox_selected: usize,
542 /// Set of package names with expanded dependency lists (for Sandbox tab tree view).
543 sandbox_tree_expanded: HashSet<String>,
544 /// Whether sandbox info has been resolved for the current session.
545 sandbox_loaded: bool,
546 /// Error message from sandbox resolution, if any.
547 sandbox_error: Option<String>,
548 /// Selected optional dependencies to install with their packages.
549 /// Maps package name -> set of selected optional dependency names.
550 selected_optdepends: std::collections::HashMap<String, std::collections::HashSet<String>>,
551 /// Current cascade removal strategy for this session.
552 cascade_mode: CascadeMode,
553 /// Cached reverse dependency report for Remove actions (populated during summary computation).
554 /// This avoids redundant resolution when switching to the Deps tab.
555 cached_reverse_deps_report: Option<crate::logic::deps::ReverseDependencyReport>,
556 },
557 /// Preflight execution screen with log and sticky sidebar.
558 PreflightExec {
559 /// Packages being processed.
560 items: Vec<PackageItem>,
561 /// Action being executed (install/remove/downgrade).
562 action: PreflightAction,
563 /// Tab to display while executing.
564 tab: PreflightTab,
565 /// Whether verbose logging is enabled.
566 verbose: bool,
567 /// Execution log lines.
568 log_lines: Vec<String>,
569 /// Whether the operation can be aborted.
570 abortable: bool,
571 /// Header chip metrics displayed in the sidebar.
572 header_chips: PreflightHeaderChips,
573 /// Execution result: `Some(true)` for success, `Some(false)` for failure, `None` if not yet completed.
574 success: Option<bool>,
575 },
576 /// Post-transaction summary with results and follow-ups.
577 PostSummary {
578 /// Whether the operation succeeded.
579 success: bool,
580 /// Number of files changed.
581 changed_files: usize,
582 /// Number of .pacnew files created.
583 pacnew_count: usize,
584 /// Number of .pacsave files created.
585 pacsave_count: usize,
586 /// Services pending restart.
587 services_pending: Vec<String>,
588 /// Snapshot label if created.
589 snapshot_label: Option<String>,
590 },
591 /// Help overlay with keybindings. Non-interactive; dismissed with Esc/Enter.
592 Help,
593 /// Confirmation dialog for removing the given items.
594 ConfirmRemove {
595 /// Package items to remove.
596 items: Vec<PackageItem>,
597 },
598 /// System update dialog with multi-select options and optional country.
599 SystemUpdate {
600 /// Whether to update Arch mirrors using reflector.
601 do_mirrors: bool,
602 /// Whether to update system packages via pacman.
603 do_pacman: bool,
604 /// Whether to force sync databases (pacman -Syyu instead of -Syu).
605 force_sync: bool,
606 /// Whether to update AUR packages via paru/yay.
607 do_aur: bool,
608 /// Whether to remove caches (pacman and AUR helper).
609 do_cache: bool,
610 /// Index into `countries` for the reflector `--country` argument.
611 country_idx: usize,
612 /// Available countries to choose from for reflector.
613 countries: Vec<String>,
614 /// Requested mirror count to fetch/rank.
615 mirror_count: u16,
616 /// Cursor row in the dialog (0..=4)
617 cursor: usize,
618 },
619 /// Arch Linux News: list of recent items with selection.
620 News {
621 /// Latest news feed items (Arch news, advisories, updates, comments).
622 items: Vec<crate::state::types::NewsFeedItem>,
623 /// Selected row index.
624 selected: usize,
625 /// Scroll offset (lines) for the news list.
626 scroll: u16,
627 },
628 /// Application announcement: markdown content displayed at startup.
629 Announcement {
630 /// Title to display in the modal header.
631 title: String,
632 /// Markdown content to display.
633 content: String,
634 /// Unique identifier for this announcement (version string or remote ID).
635 id: String,
636 /// Scroll offset (lines) for long content.
637 scroll: u16,
638 },
639 /// Available package updates: list of update entries with scroll support.
640 Updates {
641 /// Update entries with package name, old version, and new version.
642 entries: Vec<(String, String, String)>, // (name, old_version, new_version)
643 /// Scroll offset (lines) for the updates list.
644 scroll: u16,
645 /// Selected row index.
646 selected: usize,
647 },
648 /// TUI Optional Dependencies chooser: selectable rows with install status.
649 OptionalDeps {
650 /// Rows to display (pre-filtered by environment/distro).
651 rows: Vec<OptionalDepRow>,
652 /// Selected row index.
653 selected: usize,
654 },
655 /// Select which scans to run before executing the AUR scan.
656 ScanConfig {
657 /// Whether to run `ClamAV` (clamscan).
658 do_clamav: bool,
659 /// Whether to run Trivy filesystem scan.
660 do_trivy: bool,
661 /// Whether to run Semgrep static analysis.
662 do_semgrep: bool,
663 /// Whether to run `ShellCheck` on `PKGBUILD`/.install.
664 do_shellcheck: bool,
665 /// Whether to run `VirusTotal` hash lookups.
666 do_virustotal: bool,
667 /// Whether to run custom suspicious-pattern scan (PKGBUILD/.install).
668 do_custom: bool,
669 /// Whether to run aur-sleuth (LLM audit).
670 do_sleuth: bool,
671 /// Cursor row in the dialog.
672 cursor: usize,
673 },
674 /// Prompt to install `GNOME Terminal` at startup on GNOME when not present.
675 GnomeTerminalPrompt,
676 /// Setup dialog for `VirusTotal` API key.
677 VirusTotalSetup {
678 /// User-entered API key buffer.
679 input: String,
680 /// Cursor position within the input buffer.
681 cursor: usize,
682 },
683 /// Information dialog explaining the Import file format.
684 ImportHelp,
685 /// Setup dialog for startup news popup configuration.
686 NewsSetup {
687 /// Whether to show Arch news.
688 show_arch_news: bool,
689 /// Whether to show security advisories.
690 show_advisories: bool,
691 /// Whether to show AUR updates.
692 show_aur_updates: bool,
693 /// Whether to show AUR comments.
694 show_aur_comments: bool,
695 /// Whether to show official package updates.
696 show_pkg_updates: bool,
697 /// Maximum age of news items in days (7, 30, or 90).
698 max_age_days: Option<u32>,
699 /// Current cursor position (0-5 for toggles, 6-8 for date buttons).
700 cursor: usize,
701 },
702 /// Password prompt for sudo authentication.
703 PasswordPrompt {
704 /// Purpose of the password prompt.
705 purpose: PasswordPurpose,
706 /// Packages involved in the operation.
707 items: Vec<PackageItem>,
708 /// User input buffer for password.
709 input: String,
710 /// Cursor position within the input buffer.
711 cursor: usize,
712 /// Error message if password was incorrect.
713 error: Option<String>,
714 },
715}
716
717#[cfg(test)]
718mod tests {
719 #[test]
720 /// What: Confirm each `Modal` variant can be constructed and the `Default` implementation returns `Modal::None`.
721 ///
722 /// Inputs:
723 /// - No external inputs; instantiates representative variants directly inside the test.
724 ///
725 /// Output:
726 /// - Ensures `Default::default()` yields `Modal::None` and variant constructors remain stable.
727 ///
728 /// Details:
729 /// - Acts as a regression guard when fields or defaults change, catching compile-time or panicking construction paths.
730 fn modal_default_and_variants_construct() {
731 let m = super::Modal::default();
732 matches!(m, super::Modal::None);
733 let _ = super::Modal::Alert {
734 message: "hi".into(),
735 };
736 let _ = super::Modal::ConfirmInstall { items: Vec::new() };
737 let _ = super::Modal::ConfirmReinstall {
738 items: Vec::new(),
739 all_items: Vec::new(),
740 header_chips: crate::state::modal::PreflightHeaderChips::default(),
741 };
742 let _ = super::Modal::Help;
743 let _ = super::Modal::ConfirmRemove { items: Vec::new() };
744 let _ = super::Modal::SystemUpdate {
745 do_mirrors: true,
746 do_pacman: true,
747 force_sync: false,
748 do_aur: true,
749 do_cache: false,
750 country_idx: 0,
751 countries: vec!["US".into()],
752 mirror_count: 20,
753 cursor: 0,
754 };
755 let _ = super::Modal::News {
756 items: Vec::new(),
757 selected: 0,
758 scroll: 0,
759 };
760 let _ = super::Modal::OptionalDeps {
761 rows: Vec::new(),
762 selected: 0,
763 };
764 let _ = super::Modal::GnomeTerminalPrompt;
765 let _ = super::Modal::VirusTotalSetup {
766 input: String::new(),
767 cursor: 0,
768 };
769 let _ = super::Modal::ImportHelp;
770 let _ = super::Modal::PasswordPrompt {
771 purpose: super::PasswordPurpose::Install,
772 items: Vec::new(),
773 input: String::new(),
774 cursor: 0,
775 error: None,
776 };
777 let _ = super::Modal::Preflight {
778 items: Vec::new(),
779 action: super::PreflightAction::Install,
780 tab: super::PreflightTab::Summary,
781 summary: None,
782 summary_scroll: 0,
783 header_chips: super::PreflightHeaderChips::default(),
784 dependency_info: Vec::new(),
785 dep_selected: 0,
786 dep_tree_expanded: std::collections::HashSet::new(),
787 deps_error: None,
788 file_info: Vec::new(),
789 file_selected: 0,
790 file_tree_expanded: std::collections::HashSet::new(),
791 files_error: None,
792 service_info: Vec::new(),
793 service_selected: 0,
794 services_loaded: false,
795 services_error: None,
796 sandbox_info: Vec::new(),
797 sandbox_selected: 0,
798 sandbox_tree_expanded: std::collections::HashSet::new(),
799 sandbox_loaded: false,
800 sandbox_error: None,
801 selected_optdepends: std::collections::HashMap::new(),
802 cascade_mode: super::CascadeMode::Basic,
803 cached_reverse_deps_report: None,
804 };
805 }
806}