use std::fmt::Write as _;
use std::path::PathBuf;
use zeph_config::WorktreeConfig;
#[derive(Debug, Clone, Default)]
pub struct WorktreeDiskUsage {
pub total_bytes: u64,
pub per_worktree: Vec<(PathBuf, u64)>,
}
#[derive(Debug, Clone, Default)]
pub struct QuotaStatus {
pub count: usize,
pub max_worktrees: Option<usize>,
pub total_bytes: u64,
pub disk_quota_bytes: Option<u64>,
pub reclaimed: usize,
pub over_count: bool,
pub over_disk: bool,
}
impl QuotaStatus {
#[must_use]
pub fn is_over_quota(&self) -> bool {
self.over_count || self.over_disk
}
}
#[must_use]
pub fn format_usage_summary(
usage: &WorktreeDiskUsage,
count: usize,
config: &WorktreeConfig,
) -> String {
let used_mb = usage.total_bytes / 1_048_576;
let mut line = format!("{count} worktree(s), {used_mb} MB");
if let Some(max) = config.max_worktrees {
let status = if count >= max { "OVER" } else { "ok" };
let _ = write!(line, ", max_worktrees: {count}/{max} [{status}]");
}
if let Some(quota_mb) = config.disk_quota_mb {
let status = if used_mb >= quota_mb { "OVER" } else { "ok" };
let _ = write!(line, ", disk_quota_mb: {used_mb}/{quota_mb} [{status}]");
}
line
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_over_quota_true_when_over_count() {
let status = QuotaStatus {
over_count: true,
..Default::default()
};
assert!(status.is_over_quota());
}
#[test]
fn is_over_quota_true_when_over_disk() {
let status = QuotaStatus {
over_disk: true,
..Default::default()
};
assert!(status.is_over_quota());
}
#[test]
fn is_over_quota_false_when_neither() {
assert!(!QuotaStatus::default().is_over_quota());
}
#[test]
fn format_usage_summary_no_quota_configured() {
let usage = WorktreeDiskUsage {
total_bytes: 5 * 1_048_576,
per_worktree: Vec::new(),
};
let summary = format_usage_summary(&usage, 3, &WorktreeConfig::default());
assert_eq!(summary, "3 worktree(s), 5 MB");
}
#[test]
fn format_usage_summary_reports_ok_under_thresholds() {
let usage = WorktreeDiskUsage {
total_bytes: 1_048_576,
per_worktree: Vec::new(),
};
let config = WorktreeConfig {
max_worktrees: Some(5),
disk_quota_mb: Some(10),
..Default::default()
};
let summary = format_usage_summary(&usage, 1, &config);
assert!(
summary.contains("max_worktrees: 1/5 [ok]"),
"got: {summary}"
);
assert!(
summary.contains("disk_quota_mb: 1/10 [ok]"),
"got: {summary}"
);
assert!(!summary.contains("OVER"), "got: {summary}");
}
#[test]
fn format_usage_summary_reports_over_at_thresholds() {
let usage = WorktreeDiskUsage {
total_bytes: 10 * 1_048_576,
per_worktree: Vec::new(),
};
let config = WorktreeConfig {
max_worktrees: Some(2),
disk_quota_mb: Some(10),
..Default::default()
};
let summary = format_usage_summary(&usage, 2, &config);
assert!(
summary.contains("max_worktrees: 2/2 [OVER]"),
"got: {summary}"
);
assert!(
summary.contains("disk_quota_mb: 10/10 [OVER]"),
"got: {summary}"
);
}
}