use dates::Date;
use dates::calendar::RcCalendar;
use core::factories::TypeId;
use core::factories::Registry;
use core::factories::Qrc;
use std::sync::Arc;
use std::fmt::Debug;
use erased_serde as esd;
use serde as sd;
use serde_tagged as sdt;
use serde_tagged::de::BoxFnSeed;
use serde::Deserialize;
pub trait DateRule : esd::Serialize + TypeId + Sync + Send + Debug {
fn apply(&self, date: Date) -> Date;
}
pub type RcDateRule = Qrc<DateRule>;
pub type TypeRegistry = Registry<BoxFnSeed<RcDateRule>>;
impl<'de> sd::Deserialize<'de> for RcDateRule {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: sd::Deserializer<'de>
{
sdt::de::external::deserialize(deserializer, get_registry())
}
}
pub fn get_registry() -> &'static TypeRegistry {
lazy_static! {
static ref REG: TypeRegistry = {
let mut reg = TypeRegistry::new();
reg.insert("NullRule", BoxFnSeed::new(NullRule::from_serial));
reg.insert("BusinessDays", BoxFnSeed::new(BusinessDays::from_serial));
reg.insert("ModifiedFollowing", BoxFnSeed::new(ModifiedFollowing::from_serial));
reg
};
}
®
}
#[derive(Serialize, Deserialize, Debug)]
pub struct NullRule {}
impl DateRule for NullRule {
fn apply(&self, date: Date) -> Date { date }
}
impl TypeId for NullRule {
fn type_id(&self) -> &'static str { "NullRule" }
}
impl NullRule {
pub fn new() -> NullRule { NullRule { } }
pub fn from_serial<'de>(de: &mut esd::Deserializer<'de>) -> Result<RcDateRule, esd::Error> {
Ok(Qrc::new(Arc::new(NullRule::deserialize(de)?)))
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BusinessDays {
calendar: RcCalendar,
step: i32,
slip_forward: bool
}
impl TypeId for BusinessDays {
fn type_id(&self) -> &'static str { "BusinessDays" }
}
impl BusinessDays {
pub fn new_next(calendar: RcCalendar) -> BusinessDays {
BusinessDays {
calendar: calendar,
step: 0,
slip_forward: true }
}
pub fn new_prev(calendar: RcCalendar) -> BusinessDays {
BusinessDays {
calendar: calendar,
step: 0,
slip_forward: false }
}
pub fn new_step(calendar: RcCalendar, step: u32) -> BusinessDays {
BusinessDays {
calendar: calendar,
step: step as i32,
slip_forward: true }
}
pub fn new_back(calendar: RcCalendar, step: u32) -> BusinessDays {
BusinessDays {
calendar: calendar,
step: -(step as i32),
slip_forward: false }
}
pub fn from_serial<'de>(de: &mut esd::Deserializer<'de>) -> Result<RcDateRule, esd::Error> {
Ok(Qrc::new(Arc::new(BusinessDays::deserialize(de)?)))
}
}
impl DateRule for BusinessDays {
fn apply(&self, date: Date) -> Date {
self.calendar.step(date, self.step, self.slip_forward)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifiedFollowing {
calendar: RcCalendar
}
impl TypeId for ModifiedFollowing {
fn type_id(&self) -> &'static str { "ModifiedFollowing" }
}
impl ModifiedFollowing {
pub fn new(calendar: RcCalendar) -> ModifiedFollowing {
ModifiedFollowing { calendar: calendar }
}
pub fn from_serial<'de>(de: &mut esd::Deserializer<'de>) -> Result<RcDateRule, esd::Error> {
Ok(Qrc::new(Arc::new(ModifiedFollowing::deserialize(de)?)))
}
}
impl DateRule for ModifiedFollowing {
fn apply(&self, date: Date) -> Date {
let (_, m1, _) = date.ymd();
let forward = self.calendar.step(date, 0, true);
let (_, m2, _) = forward.ymd();
if m2 == m1 {
forward
} else {
self.calendar.step(date, 0, false)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use dates::calendar::WeekdayCalendar;
use std::str::FromStr;
#[test]
fn next_business_date() {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let rule = BusinessDays::new_next(calendar);
let start = Date::from_str("2017-01-01").unwrap();
let next = Date::from_str("2017-01-02").unwrap();
let step1 = rule.apply(start);
assert_eq!(step1, next);
let step2 = rule.apply(step1);
assert_eq!(step2, next);
}
#[test]
fn prev_business_date() {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let rule = BusinessDays::new_prev(calendar);
let start = Date::from_str("2017-01-01").unwrap();
let prev = Date::from_str("2016-12-30").unwrap();
let step1 = rule.apply(start);
assert_eq!(step1, prev, "step1={}:{} prev={}:{} start={}:{}",
step1.to_string(), step1.day_of_week(),
prev.to_string(), prev.day_of_week(),
start.to_string(), start.day_of_week());
let step2 = rule.apply(step1);
assert_eq!(step2, prev);
}
#[test]
fn step_forward_business_date() {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let rule = BusinessDays::new_step(calendar, 2);
let start = Date::from_str("2017-01-01").unwrap();
let step1 = rule.apply(start);
assert_eq!(step1, Date::from_str("2017-01-04").unwrap());
let step2 = rule.apply(step1);
assert_eq!(step2, Date::from_str("2017-01-06").unwrap());
let step3 = rule.apply(step2);
assert_eq!(step3, Date::from_str("2017-01-10").unwrap());
}
#[test]
fn step_back_business_date() {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let rule = BusinessDays::new_back(calendar, 2);
let start = Date::from_str("2017-01-01").unwrap();
let step1 = rule.apply(start);
assert_eq!(step1, Date::from_str("2016-12-28").unwrap());
let step2 = rule.apply(step1);
assert_eq!(step2, Date::from_str("2016-12-26").unwrap());
}
#[test]
fn modified_following() {
let calendar = RcCalendar::new(Arc::new(WeekdayCalendar{}));
let rule = ModifiedFollowing::new(calendar);
let start1 = Date::from_str("2016-12-31").unwrap();
let prev = Date::from_str("2016-12-30").unwrap();
let start2 = Date::from_str("2017-01-01").unwrap();
let next = Date::from_str("2017-01-02").unwrap();
let step1 = rule.apply(start1);
assert_eq!(step1, prev);
let step2 = rule.apply(step1);
assert_eq!(step2, prev);
let step3 = rule.apply(start2);
assert_eq!(step3, next);
let step4 = rule.apply(step3);
assert_eq!(step4, next); }
}