use std::collections::VecDeque;
pub const PAGER_MAX_LINES: usize = 10000;
#[derive(Debug)]
pub struct Pager {
lines: VecDeque<String>,
current_line: usize,
lines_per_page: usize,
}
impl Default for Pager {
fn default() -> Self {
Self::new()
}
}
impl Pager {
pub fn new() -> Self {
Self {
lines: VecDeque::new(),
current_line: 0,
lines_per_page: 24,
}
}
pub fn with_lines_per_page(mut self, lines: usize) -> Self {
self.lines_per_page = lines;
self
}
pub fn add_lines(&mut self, text: &str) {
for line in text.lines() {
if self.lines.len() >= PAGER_MAX_LINES {
self.lines.pop_front();
}
self.lines.push_back(line.to_string());
}
}
pub fn add_line(&mut self, line: impl Into<String>) {
let line = line.into();
if self.lines.len() >= PAGER_MAX_LINES {
self.lines.pop_front();
}
self.lines.push_back(line);
}
pub fn total_lines(&self) -> usize {
self.lines.len()
}
pub fn current_line(&self) -> usize {
self.current_line
}
pub fn total_pages(&self) -> usize {
(self.lines.len() + self.lines_per_page - 1) / self.lines_per_page
}
pub fn current_page(&self) -> usize {
self.current_line / self.lines_per_page
}
pub fn current_page_lines(&self) -> Vec<&str> {
let start = self.current_line;
let end = (start + self.lines_per_page).min(self.lines.len());
self.lines
.iter()
.skip(start)
.take(end - start)
.map(|s| s.as_str())
.collect()
}
pub fn next_page(&mut self) -> bool {
let next_line = (self.current_line + self.lines_per_page).min(self.lines.len() - 1);
if next_line != self.current_line {
self.current_line = next_line;
true
} else {
false
}
}
pub fn prev_page(&mut self) -> bool {
if self.current_line >= self.lines_per_page {
self.current_line -= self.lines_per_page;
true
} else {
false
}
}
pub fn next_line(&mut self) -> bool {
if self.current_line < self.lines.len() - 1 {
self.current_line += 1;
true
} else {
false
}
}
pub fn prev_line(&mut self) -> bool {
if self.current_line > 0 {
self.current_line -= 1;
true
} else {
false
}
}
pub fn go_to_start(&mut self) {
self.current_line = 0;
}
pub fn go_to_end(&mut self) {
self.current_line = self.lines.saturating_len().saturating_sub(1);
}
pub fn go_to_line(&mut self, line: usize) {
self.current_line = line.min(self.lines.saturating_len().saturating_sub(1));
}
pub fn at_start(&self) -> bool {
self.current_line == 0
}
pub fn at_end(&self) -> bool {
self.current_line >= self.lines.len().saturating_sub(1)
}
pub fn has_next_page(&self) -> bool {
self.current_line + self.lines_per_page < self.lines.len()
}
pub fn has_prev_page(&self) -> bool {
self.current_line >= self.lines_per_page
}
pub fn clear(&mut self) {
self.lines.clear();
self.current_line = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pager_basic() {
let mut pager = Pager::new().with_lines_per_page(5);
for i in 0..20 {
pager.add_line(format!("Line {}", i));
}
assert_eq!(pager.total_lines(), 20);
assert_eq!(pager.total_pages(), 4);
pager.go_to_end();
assert!(pager.at_end());
pager.go_to_start();
assert!(pager.at_start());
}
}