use std::sync::atomic::Ordering;
use ratatui::buffer::Buffer;
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
use ratatui::macros::horizontal;
use ratatui::style::{Color, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Widget;
use pagers_core::ops::Stats;
use pagers_core::output::{pretty_elapsed, pretty_size};
pub(crate) const SUMMARY_LINES: u16 = 5;
const LABEL_WIDTH: u16 = 17;
pub(crate) struct SummaryWidget<'a> {
pub stats: &'a Stats,
pub elapsed: f64,
pub label: &'a str,
pub action_sign: isize,
}
impl Widget for SummaryWidget<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let page_size = *pagers_core::pagesize::PAGE_SIZE;
let total_pages = self.stats.total_pages.load(Ordering::Relaxed);
let initial = self.stats.initial_pages_in_core.load(Ordering::Relaxed);
let action_pages = self.stats.action_pages.load(Ordering::Relaxed);
let total_files = self.stats.total_files.load(Ordering::Relaxed);
let total_dirs = self.stats.total_dirs.load(Ordering::Relaxed);
let total_size = total_pages * page_size;
let action_size = action_pages * page_size;
let signed_action = (action_pages as isize) * self.action_sign;
let resident_pages = initial.saturating_add_signed(signed_action);
let resident_size = resident_pages * page_size;
let label_style = Style::default().fg(Color::DarkGray);
let mut cap = self.label.to_string();
if let Some(c) = cap.get_mut(0..1) {
c.make_ascii_uppercase();
}
let has_action = self.action_sign != 0;
let mut rows: Vec<(String, Line)> = vec![
("Files:".into(), Line::from(format!("{total_files}"))),
("Directories:".into(), Line::from(format!("{total_dirs}"))),
];
if has_action {
rows.push((
format!("{cap} Pages:"),
pct_line(action_pages, total_pages, action_size, total_size),
));
}
rows.push((
"Resident Pages:".into(),
pct_line(resident_pages, total_pages, resident_size, total_size),
));
rows.push(("Elapsed:".into(), Line::from(pretty_elapsed(self.elapsed))));
let constraints: Vec<Constraint> = rows.iter().map(|_| Constraint::Length(1)).collect();
let line_areas = Layout::vertical(constraints).split(area);
for (i, (label_text, value_line)) in rows.into_iter().enumerate() {
let [label_area, _, value_area] =
horizontal![==LABEL_WIDTH, ==1, *=1].areas(line_areas[i]);
Line::from(Span::styled(label_text, label_style))
.alignment(Alignment::Right)
.render(label_area, buf);
value_line.render(value_area, buf);
}
}
}
fn pct_line(pages: usize, total_pages: usize, size: usize, total_size: usize) -> Line<'static> {
let pct = if total_pages > 0 {
100.0 * pages as f64 / total_pages as f64
} else {
0.0
};
let mut spans = vec![Span::raw(format!(
"{pages}/{total_pages} {}/{}",
pretty_size(size),
pretty_size(total_size)
))];
if total_pages > 0 {
spans.push(Span::raw(format!(" {pct:.3}%")));
}
Line::from(spans)
}