use crate::shared::shared;
use crate::time::calendar::{Calendar, CalendarImpl};
use crate::time::date::Date;
use crate::time::weekday::Weekday;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum JointCalendarRule {
JoinHolidays,
JoinBusinessDays,
}
pub struct JointCalendar;
impl JointCalendar {
pub fn new(calendars: Vec<Calendar>, rule: JointCalendarRule) -> Calendar {
assert!(!calendars.is_empty(), "at least one calendar required");
Calendar::from_impl(shared(Impl { rule, calendars }))
}
pub fn of_two(c1: Calendar, c2: Calendar, rule: JointCalendarRule) -> Calendar {
Self::new(vec![c1, c2], rule)
}
}
struct Impl {
rule: JointCalendarRule,
calendars: Vec<Calendar>,
}
impl CalendarImpl for Impl {
fn name(&self) -> String {
let prefix = match self.rule {
JointCalendarRule::JoinHolidays => "JoinHolidays(",
JointCalendarRule::JoinBusinessDays => "JoinBusinessDays(",
};
let mut out = String::from(prefix);
for (i, c) in self.calendars.iter().enumerate() {
if i > 0 {
out.push_str(", ");
}
out.push_str(&c.name());
}
out.push(')');
out
}
fn is_weekend(&self, w: Weekday) -> bool {
match self.rule {
JointCalendarRule::JoinHolidays => self.calendars.iter().any(|c| c.is_weekend(w)),
JointCalendarRule::JoinBusinessDays => self.calendars.iter().all(|c| c.is_weekend(w)),
}
}
fn is_weekend_on(&self, date: Date) -> bool {
match self.rule {
JointCalendarRule::JoinHolidays => self.calendars.iter().any(|c| c.is_weekend_on(date)),
JointCalendarRule::JoinBusinessDays => {
self.calendars.iter().all(|c| c.is_weekend_on(date))
}
}
}
fn is_business_day(&self, date: Date) -> bool {
match self.rule {
JointCalendarRule::JoinHolidays => {
self.calendars.iter().all(|c| c.is_business_day(date))
}
JointCalendarRule::JoinBusinessDays => {
self.calendars.iter().any(|c| c.is_business_day(date))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shared::shared;
use crate::time::calendars::target::Target;
use crate::time::calendars::weekendsonly::WeekendsOnly;
use crate::time::date::Month;
struct SwitchingWeekendCal;
impl CalendarImpl for SwitchingWeekendCal {
fn name(&self) -> String {
"switching weekend".to_string()
}
fn is_weekend(&self, weekday: Weekday) -> bool {
weekday == Weekday::Friday || weekday == Weekday::Saturday
}
fn is_weekend_on(&self, date: Date) -> bool {
let weekday = date.weekday();
if date < Date::new(29, Month::June, 2013) {
weekday == Weekday::Thursday || weekday == Weekday::Friday
} else {
weekday == Weekday::Friday || weekday == Weekday::Saturday
}
}
fn is_business_day(&self, date: Date) -> bool {
!self.is_weekend_on(date)
}
}
#[test]
fn join_holidays_is_holiday_if_any_is() {
let joint = JointCalendar::of_two(
Target::new(),
WeekendsOnly::new(),
JointCalendarRule::JoinHolidays,
);
let christmas = Date::new(25, Month::December, 2018); assert!(joint.is_holiday(christmas));
}
#[test]
fn join_business_days_is_business_if_any_is() {
let joint = JointCalendar::of_two(
Target::new(),
WeekendsOnly::new(),
JointCalendarRule::JoinBusinessDays,
);
let christmas = Date::new(25, Month::December, 2018);
assert!(joint.is_business_day(christmas));
}
#[test]
fn name_lists_members() {
let joint = JointCalendar::of_two(
Target::new(),
WeekendsOnly::new(),
JointCalendarRule::JoinHolidays,
);
assert_eq!(joint.name(), "JoinHolidays(TARGET, weekends only)");
}
#[test]
fn date_aware_weekends_are_joined() {
let switching = Calendar::from_impl(shared(SwitchingWeekendCal));
let joint = JointCalendar::of_two(
switching,
WeekendsOnly::new(),
JointCalendarRule::JoinHolidays,
);
let thursday_before_switch = Date::new(27, Month::June, 2013);
assert_eq!(thursday_before_switch.weekday(), Weekday::Thursday);
assert!(joint.is_holiday(thursday_before_switch));
assert!(joint.is_weekend_on(thursday_before_switch));
assert!(!joint.is_weekend(thursday_before_switch.weekday()));
assert!(
joint
.holiday_list(thursday_before_switch, thursday_before_switch, false)
.is_empty()
);
}
}