#[derive(Debug, Clone)]
pub struct RotationPolicy {
pub trigger: RotationTrigger,
pub max_files: u32,
pub pattern: RotationPattern,
}
#[derive(Debug, Clone, Copy)]
pub enum RotationTrigger {
Size(u64),
Duration(u64),
Messages(u64),
}
#[derive(Debug, Clone)]
pub enum RotationPattern {
Sequential,
Timestamp,
Custom(String),
}
impl RotationPolicy {
pub fn by_size(max_size_mb: u64) -> Self {
Self {
trigger: RotationTrigger::Size(max_size_mb * 1024 * 1024),
max_files: 0,
pattern: RotationPattern::Sequential,
}
}
pub fn by_duration(duration_secs: u64) -> Self {
Self {
trigger: RotationTrigger::Duration(duration_secs),
max_files: 0,
pattern: RotationPattern::Sequential,
}
}
pub fn by_messages(max_messages: u64) -> Self {
Self {
trigger: RotationTrigger::Messages(max_messages),
max_files: 0,
pattern: RotationPattern::Sequential,
}
}
pub fn with_max_files(mut self, max: u32) -> Self {
self.max_files = max;
self
}
pub fn with_timestamp_pattern(mut self) -> Self {
self.pattern = RotationPattern::Timestamp;
self
}
pub fn with_custom_pattern(mut self, pattern: impl Into<String>) -> Self {
self.pattern = RotationPattern::Custom(pattern.into());
self
}
pub fn generate_filename(&self, base_name: &str, extension: &str, index: u32) -> String {
match &self.pattern {
RotationPattern::Sequential => {
format!("{}_{:04}.{}", base_name, index, extension)
}
RotationPattern::Timestamp => {
let ts = chrono::Local::now().format("%Y%m%d_%H%M%S");
format!("{}_{}.{}", base_name, ts, extension)
}
RotationPattern::Custom(pattern) => {
let ts = chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
pattern
.replace("{n}", &format!("{:04}", index))
.replace("{ts}", &ts)
.replace("{base}", base_name)
.replace("{ext}", extension)
}
}
}
}
impl Default for RotationPolicy {
fn default() -> Self {
Self::by_size(100) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rotation_by_size() {
let policy = RotationPolicy::by_size(50);
match policy.trigger {
RotationTrigger::Size(size) => assert_eq!(size, 50 * 1024 * 1024),
_ => panic!("Wrong trigger type"),
}
}
#[test]
fn test_rotation_by_duration() {
let policy = RotationPolicy::by_duration(3600);
match policy.trigger {
RotationTrigger::Duration(secs) => assert_eq!(secs, 3600),
_ => panic!("Wrong trigger type"),
}
}
#[test]
fn test_rotation_by_messages() {
let policy = RotationPolicy::by_messages(100_000);
match policy.trigger {
RotationTrigger::Messages(count) => assert_eq!(count, 100_000),
_ => panic!("Wrong trigger type"),
}
}
#[test]
fn test_sequential_filename() {
let policy = RotationPolicy::by_size(100);
let name = policy.generate_filename("capture", "hdds", 5);
assert_eq!(name, "capture_0005.hdds");
}
#[test]
fn test_custom_pattern_filename() {
let policy = RotationPolicy::by_size(100).with_custom_pattern("{base}_part{n}.{ext}");
let name = policy.generate_filename("recording", "hdds", 3);
assert_eq!(name, "recording_part0003.hdds");
}
#[test]
fn test_max_files() {
let policy = RotationPolicy::by_size(100).with_max_files(10);
assert_eq!(policy.max_files, 10);
}
}