Last active
June 12, 2018 15:07
-
-
Save LivingInSyn/4a0b88a3cce0c58e9331f937aa520ab9 to your computer and use it in GitHub Desktop.
a nom parser for a programming challenge to parse dates in '1st of July 1983' form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate nom; | |
extern crate chrono; | |
use nom::{digit}; | |
use std::str::FromStr; | |
use chrono::{Utc, LocalResult, TimeZone}; | |
#[derive(Debug,PartialEq)] | |
struct ParsedDate { | |
pub day: i8, | |
pub month: i8, | |
pub year: i32, | |
} | |
fn month_to_int(s: &str) -> Result<i8, &'static str> { | |
match s { | |
"January" => Ok(1), | |
"February" => Ok(2), | |
"March" => Ok(3), | |
"April" => Ok(4), | |
"May" => Ok(5), | |
"June" => Ok(6), | |
"July" => Ok(7), | |
"August" => Ok(8), | |
"September" => Ok(9), | |
"October" => Ok(10), | |
"November" => Ok(11), | |
"December" => Ok(12), | |
_ => Err("Invalid Month") | |
} | |
} | |
//day of month parses out the i8 from the begining of the screen | |
named!( int8_val <&str, i8>, | |
map_res!(digit, FromStr::from_str) | |
); | |
//i32 for year | |
named!( int32_val <&str, i32>, | |
map_res!(digit, FromStr::from_str) | |
); | |
//get the month string and return an int | |
named!( month_int_val <&str, i8>, | |
map_res!(take_until!(" "), month_to_int) | |
); | |
named!(date_parser<&str, ParsedDate>, | |
do_parse!( | |
day: int8_val >> | |
take!(6) >> | |
month: month_int_val >> | |
take!(1) >> | |
year: int32_val >> | |
(ParsedDate { day, month, year}) | |
) | |
); | |
pub extern fn parse_date<'a>(input: &'a str) -> Result<chrono::Date<chrono::Utc>, &str> { | |
let mut tinput = input.to_string(); | |
tinput.push(' '); | |
match date_parser(&tinput) { | |
Ok(dateres) => { | |
match Utc.ymd_opt(dateres.1.year, dateres.1.month as u32, dateres.1.day as u32) { | |
LocalResult::Single(d) => Ok(d), | |
_ => Err("String format valid, date invalid") | |
} | |
} | |
Err(_e) => { | |
Err("Couldn't Parse Date") | |
} | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use nom::Context::Code; | |
use nom::Err::Error; | |
use nom::ErrorKind::{Digit, MapRes}; | |
#[test] | |
fn test_int8_parse() { | |
assert_eq!(int8_val("1st "), Ok(("st ", 1))); | |
assert_eq!(int8_val("31st "), Ok(("st ", 31))); | |
assert_eq!(int8_val("2nd "), Ok(("nd ", 2))); | |
assert_eq!(int8_val("3rd "), Ok(("rd ", 3))); | |
assert_eq!(int8_val("4th "), Ok(("th ", 4))); | |
} | |
#[test] | |
fn test_int32_parse() { | |
assert_eq!(int32_val("2018 "), Ok((" ", 2018))); | |
assert_eq!(int8_val("-1234 "), Err(Error(Code("-1234 ", Digit)))); | |
} | |
#[test] | |
fn test_month_parse() { | |
assert_eq!(month_int_val("July "), Ok((" ", 7))); | |
assert_eq!(month_int_val("Jul "), Err(Error(Code("Jul ", MapRes)))); | |
} | |
#[test] | |
fn parse_full_date() { | |
assert_eq!(date_parser("2nd of July 1983 "), Ok((" ", ParsedDate { year: 1983, month: 7, day: 2}))); | |
} | |
#[test] | |
fn integration_test() { | |
assert_eq!(parse_date("2nd of July 1983 "), Ok(Utc.ymd(1983, 7, 2))); | |
assert_eq!(parse_date("1st of February 2012"), Ok(Utc.ymd(2012, 2, 1))); | |
assert_eq!(parse_date("29th of February 2011"), Err("String format valid, date invalid")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment