use std::collections::HashSet;
use ratatui::prelude::*;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PackageId(pub(crate) u32);
impl PackageId {
pub fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum UserIntent {
#[default]
Default,
Install,
Remove,
Hold,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ChangeReason {
UserRequested,
Dependency,
AutoRemove,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ChangeAction {
Install,
Upgrade,
Remove,
Downgrade,
}
#[derive(Clone, Debug)]
pub struct PlannedChange {
pub package: PackageId,
pub action: ChangeAction,
pub reason: ChangeReason,
pub download_size: u64,
pub size_change: i64,
}
pub struct Clean;
pub struct Dirty;
pub struct Planned {
pub changes: Vec<PlannedChange>,
pub download_size: u64,
pub install_size_change: i64,
pub errors: Vec<String>,
}
pub trait ReadableState {}
impl ReadableState for Clean {}
impl ReadableState for Dirty {}
impl ReadableState for Planned {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PackageStatus {
Installed, NotInstalled, Upgradable, MarkedForInstall, MarkedForUpgrade, MarkedForRemove, Keep, Broken, }
impl PackageStatus {
pub fn symbol(&self) -> &'static str {
match self {
Self::Upgradable | Self::MarkedForUpgrade => "↑",
Self::MarkedForInstall => "+",
Self::MarkedForRemove => "-",
Self::Keep => "=",
Self::Installed => "·",
Self::NotInstalled => " ",
Self::Broken => "✗",
}
}
pub fn color(&self) -> Color {
match self {
Self::Upgradable => Color::Yellow,
Self::MarkedForUpgrade => Color::Green,
Self::MarkedForInstall => Color::Green,
Self::MarkedForRemove => Color::Red,
Self::Keep => Color::Blue,
Self::Installed => Color::DarkGray,
Self::NotInstalled => Color::Gray,
Self::Broken => Color::LightRed,
}
}
pub fn is_marked(&self) -> bool {
matches!(self,
Self::MarkedForInstall |
Self::MarkedForUpgrade |
Self::MarkedForRemove
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FilterCategory {
Upgradable,
MarkedChanges,
Installed,
NotInstalled,
All,
}
impl FilterCategory {
pub fn label(&self) -> &'static str {
match self {
Self::Upgradable => "Upgradable",
Self::MarkedChanges => "Marked Changes",
Self::Installed => "Installed",
Self::NotInstalled => "Not Installed",
Self::All => "All Packages",
}
}
pub fn all() -> &'static [FilterCategory] {
&[
Self::Upgradable,
Self::MarkedChanges,
Self::Installed,
Self::NotInstalled,
Self::All,
]
}
}
#[derive(Debug, Clone)]
pub struct PackageInfo {
pub id: PackageId, pub name: String, pub status: PackageStatus,
pub section: String,
pub installed_version: String,
pub candidate_version: String,
pub installed_size: u64,
pub download_size: u64,
pub description: String,
pub architecture: String,
}
impl PackageInfo {
pub fn size_str(bytes: u64) -> String {
if bytes == 0 {
return String::from("-");
}
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
if bytes >= GB {
format!("{:.1} GB", bytes as f64 / GB as f64)
} else if bytes >= MB {
format!("{:.1} MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.1} KB", bytes as f64 / KB as f64)
} else {
format!("{bytes} B")
}
}
pub fn installed_size_str(&self) -> String {
Self::size_str(self.installed_size)
}
pub fn download_size_str(&self) -> String {
Self::size_str(self.download_size)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusedPane {
Filters,
Packages,
Details,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DetailsTab {
Info,
Dependencies,
ReverseDeps,
}
#[derive(Debug, PartialEq, Eq)]
pub enum AppState {
Listing,
Searching, ShowingMarkConfirm, ShowingChanges, ShowingChangelog, ShowingSettings, ConfirmExit, Upgrading,
Done,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortBy {
Name,
Section,
InstalledVersion,
CandidateVersion,
}
impl SortBy {
pub fn label(&self) -> &'static str {
match self {
Self::Name => "Name",
Self::Section => "Section",
Self::InstalledVersion => "Installed version",
Self::CandidateVersion => "Candidate version",
}
}
pub fn all() -> &'static [SortBy] {
&[Self::Name, Self::Section, Self::InstalledVersion, Self::CandidateVersion]
}
}
#[derive(Debug, Clone)]
pub struct Settings {
pub visible_columns: HashSet<Column>,
pub sort_by: SortBy,
pub sort_ascending: bool,
}
impl Default for Settings {
fn default() -> Self {
let mut visible_columns = HashSet::new();
visible_columns.insert(Column::Status);
visible_columns.insert(Column::Name);
visible_columns.insert(Column::CandidateVersion);
Self {
visible_columns,
sort_by: SortBy::CandidateVersion,
sort_ascending: true,
}
}
}
#[derive(Debug)]
pub enum ToggleResult {
Marked {
package: PackageId,
additional: Vec<PackageId>,
},
Unmarked {
package: PackageId,
also_unmarked: Vec<PackageId>,
},
NoChange {
package: PackageId,
},
}
#[derive(Debug, Clone)]
pub enum MarkPreview {
Mark {
package_name: String,
is_upgrade: bool,
additional_installs: Vec<String>,
additional_upgrades: Vec<String>,
additional_removes: Vec<String>,
download_size: u64,
bulk_acted_ids: Vec<PackageId>,
},
Unmark {
package_name: String,
was_user_marked: bool,
also_unmarked: Vec<String>,
bulk_acted_ids: Vec<PackageId>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Column {
Status,
Name,
Section,
InstalledVersion,
CandidateVersion,
DownloadSize,
}
impl Column {
pub fn all() -> &'static [Column] {
&[
Self::Status,
Self::Name,
Self::Section,
Self::InstalledVersion,
Self::CandidateVersion,
Self::DownloadSize,
]
}
pub fn label(&self) -> &'static str {
match self {
Self::Status => "Status column (S)",
Self::Name => "Name column",
Self::Section => "Section column",
Self::InstalledVersion => "Installed version column",
Self::CandidateVersion => "Candidate version column",
Self::DownloadSize => "Download size column",
}
}
pub fn header(&self) -> &'static str {
match self {
Self::Status => "S",
Self::Name => "Package",
Self::Section => "Section",
Self::InstalledVersion => "Installed",
Self::CandidateVersion => "Candidate",
Self::DownloadSize => "Download",
}
}
pub fn width(&self, col_widths: &ColumnWidths) -> Constraint {
match self {
Self::Status => Constraint::Length(3),
Self::Name => Constraint::Min(col_widths.name),
Self::Section => Constraint::Length(col_widths.section),
Self::InstalledVersion => Constraint::Length(col_widths.installed),
Self::CandidateVersion => Constraint::Length(col_widths.candidate),
Self::DownloadSize => Constraint::Length(10),
}
}
pub fn visible_columns(settings: &Settings) -> Vec<Column> {
Self::all()
.iter()
.copied()
.filter(|col| settings.visible_columns.contains(col))
.collect()
}
}
#[derive(Debug, Clone)]
pub struct ColumnWidths {
pub name: u16,
pub section: u16,
pub installed: u16,
pub candidate: u16,
}
impl ColumnWidths {
pub fn new() -> Self {
Self {
name: 10,
section: 7,
installed: 9,
candidate: 9,
}
}
}