use std::time::Duration;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct CommitPolicy {
every_bytes: Option<u64>,
every_count: Option<u64>,
every_millis: Option<u64>,
}
impl CommitPolicy {
pub const fn manual() -> Self {
Self {
every_bytes: None,
every_count: None,
every_millis: None,
}
}
pub const fn every_bytes(bytes: u64) -> Self {
Self {
every_bytes: Some(bytes),
every_count: None,
every_millis: None,
}
}
pub const fn every_count(count: u64) -> Self {
Self {
every_bytes: None,
every_count: Some(count),
every_millis: None,
}
}
pub const fn every_millis(millis: u64) -> Self {
Self {
every_bytes: None,
every_count: None,
every_millis: Some(millis),
}
}
pub const fn and_bytes(mut self, bytes: u64) -> Self {
self.every_bytes = Some(bytes);
self
}
pub const fn and_count(mut self, count: u64) -> Self {
self.every_count = Some(count);
self
}
pub const fn and_millis(mut self, millis: u64) -> Self {
self.every_millis = Some(millis);
self
}
pub(crate) fn should_commit(&self, bytes: u64, count: u64, elapsed: Duration) -> bool {
crossed(self.every_bytes, bytes)
|| crossed(self.every_count, count)
|| crossed(self.every_millis, elapsed.as_millis() as u64)
}
}
fn crossed(threshold: Option<u64>, value: u64) -> bool {
threshold.is_some_and(|t| value >= t)
}
#[cfg(test)]
mod tests {
use super::*;
const SECOND: Duration = Duration::from_secs(1);
const ZERO: Duration = Duration::ZERO;
#[test]
fn manual_never_commits() {
let p = CommitPolicy::manual();
assert!(!p.should_commit(u64::MAX, u64::MAX, SECOND));
assert_eq!(p, CommitPolicy::default());
}
#[test]
fn byte_threshold_is_inclusive() {
let p = CommitPolicy::every_bytes(100);
assert!(!p.should_commit(99, 1, ZERO));
assert!(p.should_commit(100, 1, ZERO));
assert!(p.should_commit(101, 1, ZERO));
}
#[test]
fn count_threshold_is_inclusive() {
let p = CommitPolicy::every_count(3);
assert!(!p.should_commit(0, 2, ZERO));
assert!(p.should_commit(0, 3, ZERO));
}
#[test]
fn time_threshold_fires_only_past_the_interval() {
let p = CommitPolicy::every_millis(200);
assert!(!p.should_commit(0, 1, Duration::from_millis(199)));
assert!(p.should_commit(0, 1, Duration::from_millis(200)));
}
#[test]
fn combined_fires_on_whichever_crosses_first() {
let p = CommitPolicy::every_count(1_000).and_bytes(50);
assert!(p.should_commit(50, 1, ZERO));
assert!(!p.should_commit(49, 2, ZERO));
}
}