Add date parsing

This commit is contained in:
Savanni D'Gerinel 2023-09-16 22:01:54 -04:00
parent 1eee8280f6
commit 7d53854a98
2 changed files with 133 additions and 0 deletions

View File

@ -104,6 +104,7 @@ pub fn parse_date_field(s: &str) -> Result<Vec<Date>, Error> {
Ok(dates)
}
/*
#[cfg(test)]
mod test {
use super::*;
@ -155,3 +156,4 @@ mod test {
);
}
}
*/

View File

@ -1,4 +1,5 @@
use crate::{Color, Date, Error, GameResult};
use chrono::{Datelike, NaiveDate};
use nom::{
branch::alt,
bytes::complete::{escaped_transform, tag, take_until1},
@ -815,6 +816,81 @@ fn parse_text<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, String, E> {
}
}
enum DateSegment {
One(i32),
Two(i32, i32),
Three(i32, i32, i32),
}
fn parse_date_field<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, Vec<Date>, E> {
|input| {
let (input, first_date) = parse_date().parse(input)?;
let (input, mut more_dates) = many0(parse_date_segment())(input)?;
let mut date_segments = vec![first_date];
date_segments.append(&mut more_dates);
let mut dates = vec![];
let mut most_recent = None;
for date_segment in date_segments {
let new_date = match date_segment {
DateSegment::One(v) => match most_recent {
Some(Date::Year(_)) => Date::Year(v),
Some(Date::YearMonth(y, _)) => Date::YearMonth(y, v as u32),
Some(Date::Date(d)) => Date::Date(d.clone().with_day(v as u32).unwrap()),
None => Date::Year(v),
},
DateSegment::Two(y, m) => Date::YearMonth(y, m as u32),
DateSegment::Three(y, m, d) => {
Date::Date(chrono::NaiveDate::from_ymd_opt(y, m as u32, d as u32).unwrap())
}
};
dates.push(new_date.clone());
most_recent = Some(new_date);
}
Ok((input, dates))
}
}
fn parse_date_segment<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, DateSegment, E> {
|input| {
let (input, _) = tag(",")(input)?;
let (input, element) = parse_date().parse(input)?;
Ok((input, element))
}
}
fn parse_date<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, DateSegment, E> {
alt((parse_three(), parse_two(), parse_one()))
}
fn parse_one<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, DateSegment, E> {
parse_number().map(|year| DateSegment::One(year))
}
fn parse_two<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, DateSegment, E> {
|input| {
let (input, year) = parse_number().parse(input)?;
let (input, _) = tag("-")(input)?;
let (input, month) = parse_number().parse(input)?;
Ok((input, DateSegment::Two(year, month)))
}
}
fn parse_three<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, DateSegment, E> {
|input| {
let (input, year) = parse_number().parse(input)?;
let (input, _) = tag("-")(input)?;
let (input, month) = parse_number().parse(input)?;
let (input, _) = tag("-")(input)?;
let (input, day) = parse_number().parse(input)?;
Ok((input, DateSegment::Three(year, month, day)))
}
}
#[cfg(test)]
mod test {
use super::*;
@ -1059,3 +1135,58 @@ k<. Hard line breaks are all other linebreaks.",
parse_tree::<nom::error::VerboseError<&str>>(&data).unwrap();
}
}
#[cfg(test)]
mod date_test {
use super::*;
use cool_asserts::assert_matches;
#[test]
fn it_parses_a_year() {
assert_matches!(parse_date_field::<nom::error::VerboseError<&str>>().parse("1996"), Ok((_, date)) => {
assert_eq!(date, vec![Date::Year(1996)]);
});
}
#[test]
fn it_parses_a_month() {
assert_matches!(
parse_date_field::<nom::error::VerboseError<&str>>().parse("1996-12"),
Ok((_, date)) => assert_eq!(date, vec![Date::YearMonth(1996, 12)])
);
}
#[test]
fn it_parses_a_date() {
assert_matches!(
parse_date_field::<nom::error::VerboseError<&str>>().parse("1996-12-27"),
Ok((_, date)) => assert_eq!(date, vec![Date::Date(
NaiveDate::from_ymd_opt(1996, 12, 27).unwrap()
)])
);
}
#[test]
fn it_parses_date_continuation() {
assert_matches!(
parse_date_field::<nom::error::VerboseError<&str>>().parse("1996-12-27,28"),
Ok((_, date)) => assert_eq!(date, vec![
Date::Date(NaiveDate::from_ymd_opt(1996, 12, 27).unwrap()),
Date::Date(NaiveDate::from_ymd_opt(1996, 12, 28).unwrap())
])
);
}
#[test]
fn it_parses_date_crossing_year_boundary() {
assert_matches!(
parse_date_field::<nom::error::VerboseError<&str>>().parse("1996-12-27,28,1997-01-03,04"),
Ok((_, date)) => assert_eq!(date, vec![
Date::Date(NaiveDate::from_ymd_opt(1996, 12, 27).unwrap()),
Date::Date(NaiveDate::from_ymd_opt(1996, 12, 28).unwrap()),
Date::Date(NaiveDate::from_ymd_opt(1997, 1, 3).unwrap()),
Date::Date(NaiveDate::from_ymd_opt(1997, 1, 4).unwrap()),
])
);
}
}