use std::path::Path;
use crate::malvin_config_file::read_u64;
const DEFAULT_MAX_COUNT: u64 = 1000;
const DEFAULT_MAX_AGE_DAYS: u64 = 90;
const DEFAULT_MAX_BYTES: &str = "2GiB";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(clippy::struct_field_names)]
pub struct LogsGcConfig {
pub max_count: u64,
pub max_age_days: u64,
pub max_bytes: Option<u64>,
}
impl Default for LogsGcConfig {
fn default() -> Self {
Self {
max_count: DEFAULT_MAX_COUNT,
max_age_days: DEFAULT_MAX_AGE_DAYS,
max_bytes: parse_byte_size(DEFAULT_MAX_BYTES),
}
}
}
pub fn load_logs_gc_config(work_dir: &Path) -> LogsGcConfig {
crate::malvin_config_file::load_malvin_config(work_dir).logs
}
pub(crate) fn parse_logs_gc_config(text: &str) -> Result<LogsGcConfig, String> {
let value: toml::Value = text.parse().map_err(|e| format!("invalid TOML: {e}"))?;
let logs = value
.get("logs")
.ok_or_else(|| "missing [logs] section".to_string())?;
let max_count = read_u64(logs.get("max_count")).unwrap_or(DEFAULT_MAX_COUNT);
let max_age_days = read_u64(logs.get("max_age_days")).unwrap_or(DEFAULT_MAX_AGE_DAYS);
let max_bytes = match logs.get("max_bytes") {
Some(v) => parse_max_bytes_value(v)?,
None => parse_byte_size(DEFAULT_MAX_BYTES),
};
Ok(LogsGcConfig {
max_count,
max_age_days,
max_bytes,
})
}
pub(crate) fn parse_max_bytes_value(value: &toml::Value) -> Result<Option<u64>, String> {
match value {
toml::Value::String(s) if s.trim().is_empty() => Ok(None),
toml::Value::String(s) => parse_byte_size(s.trim())
.map(Some)
.ok_or_else(|| format!("invalid max_bytes value: {s:?}")),
_ => Err("max_bytes must be a string".to_string()),
}
}
pub fn parse_byte_size(raw: &str) -> Option<u64> {
let raw = raw.trim();
if raw.is_empty() {
return None;
}
let (num, unit) = split_byte_size(raw)?;
let n: u64 = num.parse().ok()?;
n.checked_mul(unit)
}
pub(crate) fn split_byte_size(raw: &str) -> Option<(&str, u64)> {
const UNITS: [(&str, u64); 9] = [
("TiB", 1024_u64.pow(4)),
("GiB", 1024_u64.pow(3)),
("MiB", 1024_u64.pow(2)),
("KiB", 1024),
("TB", 1000_u64.pow(4)),
("GB", 1000_u64.pow(3)),
("MB", 1000_u64.pow(2)),
("KB", 1000),
("B", 1),
];
for (suffix, mult) in UNITS {
if let Some(prefix) = raw.strip_suffix(suffix) {
return Some((prefix.trim(), mult));
}
}
None
}
#[cfg(test)]
#[path = "log_gc_config_tests.rs"]
mod log_gc_config_tests;