Skip to content

Commit

Permalink
Update time.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
Dirreke committed Mar 10, 2024
1 parent 89f27a7 commit 0b8f045
Showing 1 changed file with 61 additions and 19 deletions.
80 changes: 61 additions & 19 deletions src/append/rolling_file/policy/compound/trigger/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
//!
//! Requires the `time_trigger` feature.
#[cfg(test)]
use chrono::NaiveDateTime;
use chrono::{DateTime, Datelike, Duration, Local, TimeZone, Timelike};
#[cfg(test)]
use mock_instant::{SystemTime, UNIX_EPOCH};
Expand All @@ -13,6 +11,7 @@ use serde::de;
#[cfg(feature = "config_parsing")]
use std::fmt;
use std::sync::{Once, RwLock};
use thiserror::Error;

use crate::append::rolling_file::{policy::compound::trigger::Trigger, LogFile};
#[cfg(feature = "config_parsing")]
Expand Down Expand Up @@ -73,6 +72,12 @@ impl Default for TimeTriggerInterval {
}
}

#[derive(Debug, Error)]
enum TimeTrigerError {
#[error("too large interval in time trigger {0:?}")]
TooLargeInterval(TimeTriggerInterval),
}

#[cfg(feature = "config_parsing")]
impl<'de> serde::Deserialize<'de> for TimeTriggerInterval {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
Expand Down Expand Up @@ -193,13 +198,14 @@ impl TimeTrigger {
current: DateTime<Local>,
interval: TimeTriggerInterval,
modulate: bool,
) -> DateTime<Local> {
) -> anyhow::Result<DateTime<Local>> {
let year = current.year();
if let TimeTriggerInterval::Year(n) = interval {
let n = n as i32;
let increment = if modulate { n - year % n } else { n };
let year_new = year + increment;
return Local.with_ymd_and_hms(year_new, 1, 1, 0, 0, 0).unwrap();
let result = Local.with_ymd_and_hms(year_new, 1, 1, 0, 0, 0).unwrap();
return Ok(result);
}

if let TimeTriggerInterval::Month(n) = interval {
Expand All @@ -210,9 +216,10 @@ impl TimeTrigger {
let num_months_new = num_months + increment;
let year_new = (num_months_new / 12) as i32;
let month_new = (num_months_new) % 12 + 1;
return Local
let result = Local
.with_ymd_and_hms(year_new, month_new, 1, 0, 0, 0)
.unwrap();
return Ok(result);
}

let month = current.month();
Expand All @@ -222,14 +229,21 @@ impl TimeTrigger {
let weekday = current.weekday().num_days_from_monday() as i64; // Monday is the first day of the week
let time = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap();
let increment = if modulate { n - week0 % n } else { n };
return time + Duration::weeks(increment) - Duration::days(weekday);
let result = time
+ Duration::try_weeks(increment)
.ok_or(TimeTrigerError::TooLargeInterval(interval))?
- Duration::try_days(weekday).unwrap();
return Ok(result);
}

if let TimeTriggerInterval::Day(n) = interval {
let ordinal0 = current.ordinal0() as i64;
let time = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap();
let increment = if modulate { n - ordinal0 % n } else { n };
return time + Duration::days(increment);
let result = time
+ Duration::try_days(increment)
.ok_or(TimeTrigerError::TooLargeInterval(interval))?;
return Ok(result);
}

let hour = current.hour();
Expand All @@ -238,7 +252,10 @@ impl TimeTrigger {
.with_ymd_and_hms(year, month, day, hour, 0, 0)
.unwrap();
let increment = if modulate { n - (hour as i64) % n } else { n };
return time + Duration::hours(increment);
let result = time
+ Duration::try_hours(increment)
.ok_or(TimeTrigerError::TooLargeInterval(interval))?;
return Ok(result);
}

let min = current.minute();
Expand All @@ -247,7 +264,10 @@ impl TimeTrigger {
.with_ymd_and_hms(year, month, day, hour, min, 0)
.unwrap();
let increment = if modulate { n - (min as i64) % n } else { n };
return time + Duration::minutes(increment);
let result = time
+ Duration::try_minutes(increment)
.ok_or(TimeTrigerError::TooLargeInterval(interval))?;
return Ok(result);
}

let sec = current.second();
Expand All @@ -256,48 +276,56 @@ impl TimeTrigger {
.with_ymd_and_hms(year, month, day, hour, min, sec)
.unwrap();
let increment = if modulate { n - (sec as i64) % n } else { n };
return time + Duration::seconds(increment);
let result = time
+ Duration::try_seconds(increment)
.ok_or(TimeTrigerError::TooLargeInterval(interval))?;
return Ok(result);
}
panic!("Should not reach here!");
}

fn refresh_time(&self) {
fn refresh_time(&self) -> anyhow::Result<()> {
#[cfg(test)]
let current = {
let now: std::time::Duration = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before Unix epoch");
NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos())
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos())
.unwrap()
.naive_local()
.and_local_timezone(Local)
.unwrap()
};

#[cfg(not(test))]
let current = Local::now();
let next_time = TimeTrigger::get_next_time(current, self.interval, self.modulate);
let next_time = TimeTrigger::get_next_time(current, self.interval, self.modulate)?;
let next_roll_time = if self.max_random_delay > 0 {
let random_delay = rand::thread_rng().gen_range(0..self.max_random_delay);
next_time + Duration::seconds(random_delay as i64)
next_time
+ Duration::try_seconds(random_delay as i64)
.unwrap_or(Duration::try_milliseconds(i64::MAX).unwrap())
} else {
next_time
};
*self.next_roll_time.write().unwrap() = next_roll_time;
Ok(())
}
}

impl Trigger for TimeTrigger {
fn trigger(&self, _file: &LogFile) -> anyhow::Result<bool> {
self.initial.call_once(|| {
self.refresh_time();
});
let mut result = anyhow::Result::Ok(());
self.initial.call_once(|| result = self.refresh_time());
result?;
#[cfg(test)]
let current = {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system time before Unix epoch");
NaiveDateTime::from_timestamp_opt(now.as_secs() as i64, now.subsec_nanos())
DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos())
.unwrap()
.naive_local()
.and_local_timezone(Local)
.unwrap()
};
Expand All @@ -308,7 +336,7 @@ impl Trigger for TimeTrigger {
let is_trigger = current >= *next_roll_time;
drop(next_roll_time);
if is_trigger {
self.refresh_time();
self.refresh_time()?;
}
Ok(is_trigger)
}
Expand Down Expand Up @@ -494,6 +522,20 @@ mod test {
assert_eq!(interval, TimeTriggerInterval::Second(1));
}

#[test]
fn trigger_large_interval() {
let interval = TimeTriggerInterval::Second(i64::MAX);
let file = tempfile::tempdir().unwrap();
let logfile = LogFile {
writer: &mut None,
path: file.path(),
len: 0,
};

let trigger = TimeTrigger::new(interval, false, 0);
trigger.trigger(&logfile).unwrap_err();
}

#[test]
fn pre_process() {
let trigger = TimeTrigger::new(TimeTriggerInterval::Minute(2), true, 0);
Expand Down

0 comments on commit 0b8f045

Please sign in to comment.