Skip to content

Instantly share code, notes, and snippets.

@k0pernicus
Last active June 1, 2018 22:15
Show Gist options
  • Save k0pernicus/06bc99ca996fadb9fcd1d9e4e5c82c9c to your computer and use it in GitHub Desktop.
Save k0pernicus/06bc99ca996fadb9fcd1d9e4e5c82c9c to your computer and use it in GitHub Desktop.
/*
* 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