use anyhow::{bail, Context, Result};
use clap::Parser;
use super::query;
use crate::request_log::{self, PruneOptions};
#[derive(Parser)]
pub struct PruneCommand {
#[arg(long, value_name = "DUR")]
older_than: Option<String>,
#[arg(long, value_name = "SIZE")]
max_size: Option<String>,
#[arg(long)]
dry_run: bool,
}
impl PruneCommand {
pub fn execute(self) -> Result<()> {
if self.older_than.is_none() && self.max_size.is_none() {
bail!("nothing to prune: pass --older-than <DUR> and/or --max-size <SIZE>");
}
let older_than = match self.older_than.as_deref() {
Some(s) => Some(query::parse_since(s).context("invalid --older-than")?),
None => None,
};
let max_size = match self.max_size.as_deref() {
Some(s) => Some(request_log::parse_size(s).context("invalid --max-size")?),
None => None,
};
let path = request_log::log_file_path().context("could not resolve the log file path")?;
let outcome = request_log::prune(
&path,
&PruneOptions {
older_than,
max_size,
dry_run: self.dry_run,
},
)?;
let verb = if self.dry_run {
"Would remove"
} else {
"Removed"
};
println!(
"{verb} {} record(s); kept {} ({} → {}).",
outcome.removed,
outcome.kept,
human_bytes(outcome.bytes_before),
human_bytes(outcome.bytes_after),
);
Ok(())
}
}
fn human_bytes(n: u64) -> String {
const KB: f64 = 1024.0;
const MB: f64 = KB * 1024.0;
const GB: f64 = MB * 1024.0;
let bytes = n as f64;
if bytes >= GB {
format!("{:.1} GB", bytes / GB)
} else if bytes >= MB {
format!("{:.1} MB", bytes / MB)
} else if bytes >= KB {
format!("{:.1} KB", bytes / KB)
} else {
format!("{n} B")
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[derive(Parser)]
struct Wrapper {
#[command(subcommand)]
cmd: Wrapped,
}
#[derive(clap::Subcommand)]
enum Wrapped {
Prune(PruneCommand),
}
fn parse(args: &[&str]) -> PruneCommand {
let mut full = vec!["omni-dev", "prune"];
full.extend_from_slice(args);
match Wrapper::try_parse_from(full).unwrap().cmd {
Wrapped::Prune(cmd) => cmd,
}
}
#[test]
fn parses_flags() {
let cmd = parse(&["--older-than", "7d", "--max-size", "10mb", "--dry-run"]);
assert_eq!(cmd.older_than.as_deref(), Some("7d"));
assert_eq!(cmd.max_size.as_deref(), Some("10mb"));
assert!(cmd.dry_run);
}
#[test]
fn requires_at_least_one_bound() {
assert!(parse(&[]).execute().is_err());
}
#[test]
fn human_bytes_formats_each_scale() {
assert_eq!(human_bytes(0), "0 B");
assert_eq!(human_bytes(512), "512 B");
assert_eq!(human_bytes(1536), "1.5 KB");
assert_eq!(human_bytes(3 * 1024 * 1024), "3.0 MB");
assert_eq!(human_bytes(2 * 1024 * 1024 * 1024), "2.0 GB");
}
}