diff options
| author | pablu <me@pablu.de> | 2026-05-08 15:32:12 +0200 |
|---|---|---|
| committer | pablu <me@pablu.de> | 2026-05-08 15:32:12 +0200 |
| commit | b6487e2009cb2191ef70fc6feefb38a0b6db1543 (patch) | |
| tree | e316d8b314ec1388372e159fb60dbf0bece2469b | |
| parent | 8a1f6b186bde43323003c440910d43e0db8a7bc3 (diff) | |
some dashboard stuff
| -rw-r--r-- | src/app.rs | 13 | ||||
| -rw-r--r-- | src/gentoo/package.rs | 3 | ||||
| -rw-r--r-- | src/gentoo/portage.rs | 15 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/ui.rs | 71 |
5 files changed, 83 insertions, 22 deletions
@@ -39,6 +39,10 @@ impl<'a> App<'a> { } } + pub fn world_count(&self) -> usize { + self.portage.world_packages.len() + } + pub fn installed_packages(&self) -> Vec<Package> { self.portage.installed_packages.clone() } @@ -52,6 +56,15 @@ impl<'a> App<'a> { self.portage.installed_packages[selected_package_index].clone() } + pub fn total_installed_size(&self) -> usize { + self.portage + .installed_packages + .clone() + .into_iter() + .map(|p| p.size) + .sum() + } + pub fn toggle_search_window(&mut self) { self.showing_search_window = !self.showing_search_window; self.textarea.clear(); diff --git a/src/gentoo/package.rs b/src/gentoo/package.rs index 1d7b3f3..3ead2a4 100644 --- a/src/gentoo/package.rs +++ b/src/gentoo/package.rs @@ -11,6 +11,7 @@ pub struct Package { pub description: Option<String>, pub homepage: Option<Vec<String>>, pub license: Option<String>, + pub size: usize, } #[allow(dead_code)] @@ -19,6 +20,7 @@ impl Package { name: String, version: String, repository: String, + size: usize, homepage: Option<Vec<String>>, license: Option<String>, description: Option<String>, @@ -34,6 +36,7 @@ impl Package { description: description, homepage: homepage, license: license, + size, } } } diff --git a/src/gentoo/portage.rs b/src/gentoo/portage.rs index a28ed19..28d05da 100644 --- a/src/gentoo/portage.rs +++ b/src/gentoo/portage.rs @@ -7,6 +7,7 @@ use crate::gentoo::{Package, UseFlag}; #[derive(Debug)] pub struct Portage { pub installed_packages: Vec<Package>, + pub world_packages: Vec<String>, } fn split_pkg(input: &str) -> (&str, Option<&str>) { @@ -70,9 +71,16 @@ impl Portage { pub fn new() -> Self { Self { installed_packages: vec![], + world_packages: vec![], } } + pub fn load_world_packages(&mut self) -> io::Result<()> { + let world = fs::read_to_string("/var/lib/portage/world")?; + self.world_packages = world.split_whitespace().map(|s| s.to_string()).collect(); + Ok(()) + } + pub fn load_installed_packages(&mut self) -> io::Result<()> { let categories = fs::read_dir("/var/db/pkg")?; @@ -113,7 +121,6 @@ impl Portage { }) .collect(); - // let category = fs::read_to_string(pkg.path().join("CATEGORY"))?; let repository = fs::read_to_string(pkg.path().join("repository"))? .trim() .to_string(); @@ -136,6 +143,11 @@ impl Portage { Err(_) => None, }; + let size: usize = match fs::read_to_string(pkg.path().join("SIZE")) { + Ok(size) => size.trim().parse().unwrap_or(0), + Err(_) => 0, + }; + let repo_path = PathBuf::from("/var/db/repos/") .join(&repository) .join(cat_name.to_str().unwrap()) @@ -148,6 +160,7 @@ impl Portage { format!("{}/{}", cat_name.to_str().unwrap(), pkg_name), pkg_version.unwrap().into(), repository, + size, homepage, license, description, diff --git a/src/main.rs b/src/main.rs index 1c25018..0835fb7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,14 +37,13 @@ fn main() -> color_eyre::Result<()> { let mut tui = init_tui()?; let mut p = Portage::new(); + p.load_world_packages()?; p.load_installed_packages()?; let mut app = App::new(p); run(&mut tui, &mut app)?; - // restore_tui()?; - Ok(()) } @@ -1,6 +1,6 @@ use ratatui::{ Frame, - layout::{Alignment, Constraint, Direction, Layout}, + layout::{Alignment, Constraint, Direction, Layout, Spacing}, prelude::Rect, style::{Color, Modifier, Style, Stylize}, text::{Line, Span}, @@ -29,30 +29,30 @@ fn render_dashboard(frame: &mut Frame, app: &mut App) { let constraints = [ Constraint::Length(9), - Constraint::Percentage(10), - Constraint::Percentage(10), - Constraint::Percentage(10), + Constraint::Percentage(30), Constraint::Fill(1), Constraint::Length(3), ]; let layout = Layout::vertical(constraints); - let [ - logo_top, - top_stats, - middle_stats, - bottom_stats, - _fill, - key_hints, - ] = frame.area().layout(&layout); + let [logo_top, stats, _fill, key_hints] = frame.area().layout(&layout); + + let [stats_top, stats_middle, stats_bottom] = Layout::vertical([ + Constraint::Fill(1), + Constraint::Fill(1), + Constraint::Fill(1), + ]) + .spacing(Spacing::Overlap(1)) + .areas(stats); let splits = Layout::horizontal([ Constraint::Fill(1), Constraint::Fill(1), Constraint::Fill(1), ]) - .split(top_stats); + .spacing(Spacing::Overlap(1)) + .split(stats_top); let logo = Paragraph::new(logo); frame.render_widget(logo, logo_top); @@ -74,9 +74,11 @@ fn render_dashboard(frame: &mut Frame, app: &mut App) { Constraint::Fill(1), Constraint::Fill(1), ]) - .split(middle_stats); + .spacing(Spacing::Overlap(1)) + .split(stats_middle); - let world_packages = create_stats("World Packages", "203"); + let world_package_count = app.world_count().to_string(); + let world_packages = create_stats("World Packages", &world_package_count); let pending_updates = create_stats("Pending Updates", "16"); let last_emerge = create_stats("Last Emerge", "11:24 today"); @@ -89,9 +91,12 @@ fn render_dashboard(frame: &mut Frame, app: &mut App) { Constraint::Fill(1), Constraint::Fill(1), ]) - .split(bottom_stats); + .spacing(Spacing::Overlap(1)) + .split(stats_bottom); - let installed_size = create_stats("Installed Size", "38.2 GB"); + let installed_size = human_size(app.total_installed_size()); + + let installed_size = create_stats("Installed Size", &installed_size); let distfiles_cache = create_stats("Distfiles Cache", "12.8 GB"); let portage_news = create_stats("Portage News", "3 unread"); @@ -100,13 +105,35 @@ fn render_dashboard(frame: &mut Frame, app: &mut App) { frame.render_widget(portage_news, splits[2]); } +fn human_size(bytes: usize) -> String { + const UNITS: [&str; 5] = ["B", "KB", "MB", "GB", "TB"]; + + let mut size = bytes as f64; + let mut unit = 0; + + while size >= 1024.0 && unit < UNITS.len() - 1 { + size /= 1024.0; + unit += 1; + } + + if unit == 0 { + format!("{} {}", bytes, UNITS[unit]) + } else { + format!("{:.1} {}", size, UNITS[unit]) + } +} + fn create_stats<'a>(title: &'a str, value: &'a str) -> Paragraph<'a> { Paragraph::new(vec![ Line::from(Span::styled(title, Style::default().bold())), Line::from(""), Line::from(value), ]) - .block(Block::default().borders(Borders::ALL)) + .block( + Block::default() + .borders(Borders::ALL) + .merge_borders(ratatui::symbols::merge::MergeStrategy::Exact), + ) .alignment(Alignment::Center) } @@ -202,8 +229,14 @@ fn render_package_metadata(frame: &mut Frame, area: Rect, app: &mut App) { Span::raw(pkg.description.as_deref().unwrap_or("Unknown")), ]); + let size = Line::from_iter([ + Span::styled("Size: ", bold_style), + Span::raw(human_size(pkg.size)), + ]); + + let mut lines = vec![maintainer, version, repository, license, description, size]; + let mut homepage = homepage_lines(&pkg, bold_style); - let mut lines = vec![maintainer, version, repository, license, description]; lines.append(&mut homepage); let paragraph = Paragraph::new(lines) |
