use crate::errors::QlResult;
use crate::fail;
use crate::patterns::observable::AsObservable;
use crate::settings::Settings;
use crate::time::date::Date;
pub trait Event: AsObservable {
fn date(&self) -> Date;
fn has_occurred(
&self,
settings: &Settings<Date>,
ref_date: Option<Date>,
include_ref_date: Option<bool>,
) -> QlResult<bool>;
}
pub fn event_has_occurred(
date: Date,
settings: &Settings<Date>,
ref_date: Option<Date>,
include_ref_date: Option<bool>,
) -> QlResult<bool> {
let reference = reference_date(settings, ref_date)?;
let include = include_ref_date.unwrap_or_else(|| settings.include_reference_date_events());
Ok(if include {
date < reference
} else {
date <= reference
})
}
pub(crate) fn reference_date(settings: &Settings<Date>, ref_date: Option<Date>) -> QlResult<Date> {
match ref_date.or_else(|| settings.evaluation_date()) {
Some(reference) => Ok(reference),
None => fail!("no evaluation date set: an event needs a reference date"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::patterns::observable::Observable;
use crate::time::date::Month;
struct SimpleEvent {
date: Date,
observable: Observable,
}
impl SimpleEvent {
fn new(date: Date) -> Self {
SimpleEvent {
date,
observable: Observable::new(),
}
}
}
impl AsObservable for SimpleEvent {
fn observable(&self) -> &Observable {
&self.observable
}
}
impl Event for SimpleEvent {
fn date(&self) -> Date {
self.date
}
fn has_occurred(
&self,
settings: &Settings<Date>,
ref_date: Option<Date>,
include_ref_date: Option<bool>,
) -> QlResult<bool> {
event_has_occurred(self.date, settings, ref_date, include_ref_date)
}
}
fn today() -> Date {
Date::new(7, Month::July, 2026)
}
#[test]
fn an_event_occurs_once_the_reference_date_passes_it() {
let settings = Settings::new();
let event = SimpleEvent::new(today());
assert!(
!event
.has_occurred(&settings, Some(today() - 1), None)
.unwrap()
);
assert!(
event
.has_occurred(&settings, Some(today() + 1), None)
.unwrap()
);
}
#[test]
fn an_event_on_the_reference_date_occurs_unless_reference_date_events_are_included() {
let settings = Settings::new();
let event = SimpleEvent::new(today());
settings.set_include_reference_date_events(false);
assert!(event.has_occurred(&settings, Some(today()), None).unwrap());
settings.set_include_reference_date_events(true);
assert!(!event.has_occurred(&settings, Some(today()), None).unwrap());
}
#[test]
fn an_explicit_include_ref_date_overrides_the_settings_flag() {
let settings = Settings::new();
let event = SimpleEvent::new(today());
settings.set_include_reference_date_events(false);
assert!(
!event
.has_occurred(&settings, Some(today()), Some(true))
.unwrap()
);
settings.set_include_reference_date_events(true);
assert!(
event
.has_occurred(&settings, Some(today()), Some(false))
.unwrap()
);
}
#[test]
fn the_evaluation_date_stands_in_for_a_missing_reference_date() {
let settings = Settings::new();
settings.set_evaluation_date(today() + 1);
let event = SimpleEvent::new(today());
assert!(event.has_occurred(&settings, None, None).unwrap());
}
#[test]
fn an_unset_evaluation_date_is_an_error() {
let settings = Settings::new();
let event = SimpleEvent::new(today());
assert!(event.has_occurred(&settings, None, None).is_err());
assert!(event.has_occurred(&settings, Some(today()), None).is_ok());
}
}