diff --git a/src/commands.rs b/src/commands.rs index e523e66..5dc439b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,5 +1,15 @@ use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +#[derive(Debug)] +pub struct Delta { + pub years: i32, + pub months: i32, + pub weeks: i32, + pub days: i32, + pub hours: i32, + pub minutes: i32, +} + #[derive(Debug)] struct DateDelta { years: i32, diff --git a/src/parse.rs b/src/parse.rs index f353408..7501bde 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,13 +1,15 @@ use std::str::FromStr; -use chrono::{NaiveDate, NaiveTime}; +use chrono::{Duration, NaiveDate, NaiveTime}; use nom::branch::Alt; use nom::bytes::complete::{take_while1, take_while_m_n}; use nom::character::complete::{char, digit1, newline}; -use nom::combinator::{eof, fail, map_opt, map_res}; +use nom::combinator::{eof, fail, map_opt, map_res, opt, value}; use nom::sequence::terminated; use nom::{IResult, Parser}; +use crate::commands::Delta; + fn line_ending(i: &str) -> IResult<&str, ()> { (newline.map(|_| ()), eof.map(|_| ())).choice(i) } @@ -50,6 +52,40 @@ fn time(i: &str) -> IResult<&str, NaiveTime> { } } +fn single_delta(i: &str) -> IResult<&str, i32> { + let (i, sign) = (value(1, char('+')), value(-1, char('-'))).choice(i)?; + let (i, amount) = opt(number::)(i)?; + let amount = amount.unwrap_or(1); + Ok((i, sign * amount)) +} + +fn opt_single_delta(i: &str) -> IResult<&str, i32> { + let (i, delta) = opt(single_delta::)(i)?; + let delta = delta.unwrap_or(0); + Ok((i, delta)) +} + +fn delta(i: &str) -> IResult<&str, Delta> { + // TODO Require at least one field? + let (i, years) = opt_single_delta::<'y'>(i)?; + let (i, months) = opt_single_delta::<'m'>(i)?; + let (i, weeks) = opt_single_delta::<'w'>(i)?; + let (i, days) = opt_single_delta::<'d'>(i)?; + let (i, hours) = opt_single_delta::<'h'>(i)?; + let (i, minutes) = opt_single_delta::<'m'>(i)?; + Ok(( + i, + Delta { + years, + months, + weeks, + days, + hours, + minutes, + }, + )) +} + #[cfg(test)] mod tests { use super::*;