use crate::constants::limits::{
DEFAULT_SIZE_WARNING_THRESHOLD_KB, DEFAULT_TASK_COUNT_WARNING_THRESHOLD,
};
use std::path::Path;
use anyhow::{Context, Result};
#[derive(Debug, Clone, Copy)]
pub struct QueueSizeInfo {
pub file_size_kb: u64,
pub task_count: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct SizeCheckResult {
pub exceeds_size_threshold: bool,
pub exceeds_count_threshold: bool,
pub info: QueueSizeInfo,
pub size_threshold_kb: u32,
pub count_threshold: u32,
}
pub fn check_queue_size(
queue_path: &Path,
task_count: usize,
size_threshold_kb: u32,
count_threshold: u32,
) -> Result<SizeCheckResult> {
let metadata = std::fs::metadata(queue_path)
.with_context(|| format!("read metadata for {}", queue_path.display()))?;
let file_size_bytes = metadata.len();
let file_size_kb = file_size_bytes / 1024;
let exceeds_size_threshold = file_size_kb > u64::from(size_threshold_kb);
let exceeds_count_threshold = task_count > count_threshold as usize;
Ok(SizeCheckResult {
exceeds_size_threshold,
exceeds_count_threshold,
info: QueueSizeInfo {
file_size_kb,
task_count,
},
size_threshold_kb,
count_threshold,
})
}
pub fn print_size_warning_if_needed(result: &SizeCheckResult, quiet: bool) {
if quiet {
return;
}
if !result.exceeds_size_threshold && !result.exceeds_count_threshold {
return;
}
eprintln!();
eprintln!("⚠️ Queue size warning:");
if result.exceeds_size_threshold {
eprintln!(
" Queue file size is {}KB (threshold: {}KB)",
result.info.file_size_kb, result.size_threshold_kb
);
}
if result.exceeds_count_threshold {
eprintln!(
" Queue has {} tasks (threshold: {})",
result.info.task_count, result.count_threshold
);
}
eprintln!();
eprintln!(" Consider running maintenance commands:");
eprintln!(" ralph queue archive # Move completed tasks to the done archive");
eprintln!(" ralph queue prune # Remove old tasks from the done archive");
eprintln!();
}
pub fn size_threshold_or_default(threshold: Option<u32>) -> u32 {
threshold.unwrap_or(DEFAULT_SIZE_WARNING_THRESHOLD_KB)
}
pub fn count_threshold_or_default(threshold: Option<u32>) -> u32 {
threshold.unwrap_or(DEFAULT_TASK_COUNT_WARNING_THRESHOLD)
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn check_queue_size_detects_size_threshold() -> Result<()> {
let temp = TempDir::new()?;
let queue_path = temp.path().join("queue.json");
let content = "x".repeat(1024);
std::fs::write(&queue_path, content)?;
let result = check_queue_size(&queue_path, 10, 0, 100)?;
assert!(result.exceeds_size_threshold);
assert!(!result.exceeds_count_threshold);
assert_eq!(result.info.file_size_kb, 1);
Ok(())
}
#[test]
fn check_queue_size_detects_count_threshold() -> Result<()> {
let temp = TempDir::new()?;
let queue_path = temp.path().join("queue.json");
std::fs::write(&queue_path, r#"{"tasks": []}"#)?;
let result = check_queue_size(&queue_path, 15, 1000, 10)?;
assert!(!result.exceeds_size_threshold);
assert!(result.exceeds_count_threshold);
assert_eq!(result.info.task_count, 15);
Ok(())
}
#[test]
fn check_queue_size_no_threshold_exceeded() -> Result<()> {
let temp = TempDir::new()?;
let queue_path = temp.path().join("queue.json");
std::fs::write(&queue_path, r#"{"tasks": []}"#)?;
let result = check_queue_size(&queue_path, 10, 10000, 5000)?;
assert!(!result.exceeds_size_threshold);
assert!(!result.exceeds_count_threshold);
Ok(())
}
#[test]
fn check_queue_size_detects_both_thresholds() -> Result<()> {
let temp = TempDir::new()?;
let queue_path = temp.path().join("queue.json");
let content = "x".repeat(2048);
std::fs::write(&queue_path, content)?;
let result = check_queue_size(&queue_path, 1000, 1, 500)?;
assert!(result.exceeds_size_threshold);
assert!(result.exceeds_count_threshold);
Ok(())
}
#[test]
fn threshold_helpers_return_defaults() {
assert_eq!(
size_threshold_or_default(None),
DEFAULT_SIZE_WARNING_THRESHOLD_KB
);
assert_eq!(
count_threshold_or_default(None),
DEFAULT_TASK_COUNT_WARNING_THRESHOLD
);
}
#[test]
fn threshold_helpers_return_configured() {
assert_eq!(size_threshold_or_default(Some(1000)), 1000);
assert_eq!(count_threshold_or_default(Some(1000)), 1000);
}
}