Skip to content

Instantly share code, notes, and snippets.

@LivingInSyn
Last active June 12, 2018 15:07
Show Gist options
  • Save LivingInSyn/4a0b88a3cce0c58e9331f937aa520ab9 to your computer and use it in GitHub Desktop.
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
#[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