use nom::{
Parser,
bytes::complete::tag_no_case,
character::complete::{multispace1, space1},
combinator::opt,
multi::separated_list1,
};
use suffix::Suffix;
use time::RelativeTime;
use units::RelativeUnit;
use crate::error::FlexitimeResult2;
mod suffix;
pub mod time;
pub mod units;
pub fn parse_relative_time(input: &str) -> FlexitimeResult2<&str, RelativeTime> {
let (input, _) = opt((tag_no_case("in"), multispace1)).parse(input)?;
let (input, (units, suffix)) = (
separated_list1(space1, units::parse_unit),
suffix::parse_suffix,
)
.parse(input)?;
let mut time = RelativeTime::default();
for unit in units {
match unit.unit {
RelativeUnit::Seconds => time.seconds = Some(unit.amount),
RelativeUnit::Minutes => time.minutes = Some(unit.amount),
RelativeUnit::Hours => time.hours = Some(unit.amount),
RelativeUnit::Days => time.days = Some(unit.amount),
RelativeUnit::Weeks => time.weeks = Some(unit.amount),
RelativeUnit::Months => time.months = Some(unit.amount),
RelativeUnit::Years => time.years = Some(unit.amount),
}
}
if let Some(suffix) = suffix
&& suffix == Suffix::Ago
{
time.negative = true;
}
Ok((input, time))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
assert_eq!(
parse_relative_time("2d 1h"),
Ok(("", RelativeTime::new().days(2).hours(1)))
);
}
#[test]
fn test_parse_complex() {
assert_eq!(
parse_relative_time("3y 2mo 2d 1h 5m"),
Ok((
"",
RelativeTime::new()
.years(3)
.months(2)
.days(2)
.hours(1)
.minutes(5)
))
);
}
#[test]
fn test_parse_misordered() {
assert_eq!(
parse_relative_time("5m 3d 2mo"),
Ok(("", RelativeTime::new().minutes(5).days(3).months(2)))
);
}
#[test]
fn test_parse_spaced_longhand() {
assert_eq!(
parse_relative_time("5 months 3 days 1 minute ago"),
Ok(("", RelativeTime::new().months(5).days(3).minutes(1).ago()))
);
}
#[test]
fn test_parse_in_prefix() {
assert_eq!(
parse_relative_time("in 5 months 3 days 1 minute"),
Ok(("", RelativeTime::new().months(5).days(3).minutes(1)))
);
}
#[test]
fn test_parse_negative() {
assert_eq!(
parse_relative_time("2d 1h ago"),
Ok(("", RelativeTime::new().days(2).hours(1).ago()))
);
}
}