From ff244b2e9b7705534ea37ff727be205274ea9ae3 Mon Sep 17 00:00:00 2001 From: Teddy Wing Date: Sun, 21 Oct 2018 19:51:48 +0200 Subject: Add `trial` module, calculate days remaining This new module will contain functions to calculate trial days. Here we implement a function to calculate the days remaining in a trial period. If there are less than 0 days remaining, it returns an error result. Couldn't use 'error-chain' because it was complaining about `Debug` and `PartialEq` not being defined. Don't know if it's possible to implement those traits on 'error-chain''s ErrorKind`, but it was complicated enough that I ended up not bothering. Went with 'quick_error' instead to create a custom error type without having to `impl` all the required traits. --- src/errors.rs | 10 +++++++ src/lib.rs | 5 ++++ src/trial.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/trial.rs (limited to 'src') diff --git a/src/errors.rs b/src/errors.rs index beafc9f..5f09c27 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,3 +5,13 @@ error_chain! { Xdg(xdg::BaseDirectoriesError); } } + +quick_error! { + #[derive(Debug, PartialEq)] + pub enum DurationError { + NegativeDuration(duration: i32) { + description("negative duration") + display("negative duration: '{}'", duration) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 801591c..8e23367 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![recursion_limit="128"] extern crate autopilot; +extern crate chrono; extern crate cocoa; #[macro_use] @@ -19,6 +20,9 @@ extern crate log; #[macro_use] extern crate objc; +#[macro_use] +extern crate quick_error; + #[macro_use] extern crate serde_derive; extern crate stderrlog; @@ -31,6 +35,7 @@ mod config; mod errors; mod key_code; mod parser; +mod trial; use parser::{Action, HeadphoneButton, MapAction, MapGroup, MapKind}; diff --git a/src/trial.rs b/src/trial.rs new file mode 100644 index 0000000..9604f62 --- /dev/null +++ b/src/trial.rs @@ -0,0 +1,86 @@ +use chrono::{DateTime, Local, TimeZone}; + +use errors::DurationError; + +// Start timestamp on October 1 at 23h +// Trial should be valid until November 1 00h + + +const DAYS_REMAINING: u8 = 30; + +fn days_remaining( + start: DateTime, + now: DateTime, + days_available: u8, +) -> Result { + let duration = (now.date() - start.date()).num_days() as u8; + + if duration > days_available { + Err( + DurationError::NegativeDuration(days_available as i32 - duration as i32) + ) + } else { + Ok(days_available - duration) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn days_remaining_counts_days_remaining_from_start_date() { + let remaining = days_remaining( + Local.ymd(2018, 10, 1).and_hms(23, 1, 0), + Local.ymd(2018, 10, 1).and_hms(23, 30, 0), + 30, + ); + + assert_eq!(remaining, Ok(30)); + } + + #[test] + fn days_remaining_with_middle_date() { + let remaining = days_remaining( + Local.ymd(2018, 10, 1).and_hms(23, 1, 0), + Local.ymd(2018, 10, 22).and_hms(15, 0, 0), + 30, + ); + + assert_eq!(remaining, Ok(9)); + } + + #[test] + fn days_remaining_on_last_day_is_0() { + let remaining = days_remaining( + Local.ymd(2018, 10, 1).and_hms(23, 1, 0), + Local.ymd(2018, 10, 31).and_hms(23, 30, 0), + 30, + ); + + assert_eq!(remaining, Ok(0)); + } + + #[test] + fn days_remaining_on_day_following_last_day_is_negative_duration_error() { + let remaining = days_remaining( + Local.ymd(2018, 10, 1).and_hms(23, 1, 0), + Local.ymd(2018, 11, 1).and_hms(0, 0, 0), + 30, + ); + + assert_eq!(remaining, Err(DurationError::NegativeDuration(-1))); + } + + #[test] + fn days_remaining_after_last_day_is_negative_duration_error() { + let remaining = days_remaining( + Local.ymd(2018, 10, 1).and_hms(23, 1, 0), + Local.ymd(2018, 11, 5).and_hms(0, 0, 0), + 30, + ); + + assert_eq!(remaining, Err(DurationError::NegativeDuration(-5))); + } +} -- cgit v1.2.3