Last active
June 1, 2018 22:15
-
-
Save k0pernicus/06bc99ca996fadb9fcd1d9e4e5c82c9c to your computer and use it in GitHub Desktop.
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
/* | |
* Solution to the exercise here: https://github.com/hello-rust/show/issues/36 | |
*/ | |
use std::collections::HashMap; | |
pub struct Date { | |
day: u32, | |
month: u32, | |
year: u32, | |
} | |
fn parse_day(day: &str) -> Option<u32> { | |
let nb: String = day.chars().take_while(|c| c.is_digit(10)).collect(); | |
// Check first the digits | |
match nb.parse::<u32>() { | |
Ok(e @ 1...31) => { | |
// Check the associated number extension | |
match (e, &day[day.len() - 2..]) { | |
(1, "st") | (2, "nd") | (3, "rd") | (4...31, "th") => Some(e), | |
_ => None, | |
} | |
} | |
_ => None, | |
} | |
} | |
fn parse_month(month: &str) -> Option<u32> { | |
let months: HashMap<&str, u32> = [ | |
("january", 1), | |
("february", 2), | |
("march", 3), | |
("april", 4), | |
("may", 5), | |
("june", 6), | |
("july", 7), | |
("august", 8), | |
("september", 9), | |
("october", 10), | |
("november", 11), | |
("december", 12), | |
].iter().cloned().collect(); | |
match months.get(month.to_lowercase().as_str()) { | |
Some(&e) => Some(e), | |
None => None | |
} | |
} | |
fn parse_year(year: &str) -> Option<u32> { | |
match year.parse::<u32>() { | |
Ok(e) => Some(e), | |
_ => None | |
} | |
} | |
fn parse(position: usize, string: &str) -> Option<u32> { | |
match position { | |
0 => parse_day(string), | |
1 => parse_month(string), | |
2 => parse_year(string), | |
_ => None, | |
} | |
} | |
impl Date { | |
pub fn from(string: &str) -> Option<Date> { | |
if string.trim().len() == 0 { | |
return None; | |
} | |
// Get all tokens except the 'of' token | |
let tokens: Vec<&str> = string.trim().split(" ").filter(|s| *s != "of").collect(); | |
// Check if it remains three tokens | |
if tokens.len() != 3 { | |
return None; | |
} | |
let parsing_result: Vec<Option<u32>> = tokens.iter().enumerate().map(|(p, s)| parse(p, s)).collect(); | |
if parsing_result.iter().any(|x| x.is_none()) { | |
return None; | |
} | |
Some( | |
Date { | |
day: parsing_result[0].unwrap(), | |
month: parsing_result[1].unwrap(), | |
year: parsing_result[2].unwrap(), | |
} | |
) | |
} | |
pub fn display(&self) -> String { | |
format!("{}/{}/{}", self.day, self.month, self.year) | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::{Date, parse_day, parse_month, parse_year}; | |
#[test] | |
fn test_parse_day() { | |
assert_eq!(parse_day("22th").unwrap(), 22); | |
assert_eq!(parse_day("1st").unwrap(), 1); | |
assert_eq!(parse_day("31th").unwrap(), 31); | |
assert!(parse_day("33th").is_none()); | |
assert!(parse_day("1th").is_none()); | |
assert!(parse_day("31nd").is_none()); | |
} | |
#[test] | |
fn test_parse_month() { | |
assert_eq!(parse_month("January").unwrap(), 1); | |
assert_eq!(parse_month("December").unwrap(), 12); | |
assert!(parse_month("Something").is_none()); | |
} | |
#[test] | |
fn test_parse_year() { | |
assert_eq!(parse_year("2018").unwrap(), 2018); | |
assert_eq!(parse_year("0").unwrap(), 0); | |
assert!(parse_year("-1").is_none()); | |
} | |
#[test] | |
fn good_behaviour() { | |
let good_date_str: &str = "15th of May 2015"; | |
let good_date = Date::from(good_date_str).unwrap(); | |
assert_eq!(good_date.display(), String::from("15/5/2015")); | |
let good_date_str: &str = " 1st of June 2018"; | |
let good_date = Date::from(good_date_str).unwrap(); | |
assert_eq!(good_date.display(), String::from("1/6/2018")); | |
let good_date_str: &str = "2nd of July 1983 "; | |
let good_date = Date::from(good_date_str).unwrap(); | |
assert_eq!(good_date.display(), String::from("2/7/1983")); | |
} | |
#[test] | |
fn bad_behaviour() { | |
let bad_data_str: &str = ""; | |
assert!(Date::from(bad_data_str).is_none()); | |
let bad_data_str: &str = "22th of July"; | |
assert!(Date::from(bad_data_str).is_none()); | |
let bad_data_str: &str = "32th of July 2018"; | |
assert!(Date::from(bad_data_str).is_none()); | |
let bad_data_str: &str = "2th of July -1"; | |
assert!(Date::from(bad_data_str).is_none()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment