use crate::config::Config;
use std::io::{self, IsTerminal, Write};
pub fn should_use_pager(config: &Config, output_lines: usize) -> bool {
if config.no_pager {
return false;
}
if !io::stdout().is_terminal() {
return false;
}
let terminal_height = get_terminal_height().unwrap_or(24);
output_lines > (terminal_height.saturating_sub(2))
}
fn get_terminal_height() -> Option<usize> {
terminal_size::terminal_size().map(|(_, terminal_size::Height(h))| h as usize)
}
pub fn write_with_pager(config: &Config, content: &str) -> io::Result<()> {
let lines = content.lines().count();
if should_use_pager(config, lines) {
if let Ok(pager_cmd) = std::env::var("PAGER") {
if let Err(e) = write_to_external_pager(&pager_cmd, content) {
tracing::debug!(
"External pager '{}' failed: {}, falling back to minus",
pager_cmd,
e
);
write_to_minus_pager(config, content)?;
}
} else {
write_to_minus_pager(config, content)?;
}
} else {
print!("{}", content);
io::stdout().flush()?;
}
Ok(())
}
fn write_to_external_pager(pager_cmd: &str, content: &str) -> io::Result<()> {
use std::process::{Command, Stdio};
let mut child = Command::new("sh")
.arg("-c")
.arg(pager_cmd)
.stdin(Stdio::piped())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
stdin.write_all(content.as_bytes())?;
stdin.flush()?;
}
child.wait()?;
Ok(())
}
fn write_to_minus_pager(config: &Config, content: &str) -> io::Result<()> {
use minus::{LineNumbers, Pager};
let pager = Pager::new();
let _ = pager.set_line_numbers(LineNumbers::Disabled);
if config.no_color {
let _ = pager.set_prompt("-- Press q to quit, / to search --");
} else {
let _ = pager.set_prompt("\x1b[7m-- Press q to quit, / to search --\x1b[0m");
}
let _ = pager.push_str(content);
minus::page_all(pager).map_err(|e| io::Error::other(format!("Pager error: {}", e)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::OutputFormat;
use std::time::Duration;
fn test_config(no_pager: bool, no_color: bool) -> Config {
Config {
endpoint: "http://localhost:3000".to_string(),
timeout: Duration::from_secs(30),
format: OutputFormat::Pretty,
no_color,
no_header: false,
no_pager,
}
}
#[test]
fn test_should_use_pager_disabled() {
let config = test_config(true, false);
assert!(!should_use_pager(&config, 100));
}
#[test]
fn test_should_use_pager_short_output() {
let config = test_config(false, false);
assert!(!should_use_pager(&config, 5));
}
#[test]
fn test_get_terminal_height() {
if let Some(height) = get_terminal_height() {
assert!(height > 0);
assert!(height < 1000); }
}
}