Created
November 6, 2018 19:34
-
-
Save FreeMasen/3382db944429066a9593519b6f30f39c to your computer and use it in GitHub Desktop.
This file contains hidden or 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; | |
| type Duration = Vec<(f32, DurationUnit)>; | |
| pub fn parse(s: &str) -> Result<Duration, String> { | |
| match parse_duration(s) { | |
| Ok(pair) => { | |
| Ok(pair.1) | |
| }, | |
| Err(e) => Err(format!("Error parsing duration {:?}", e)), | |
| } | |
| } | |
| named!(parse_duration<&str, Duration>, | |
| do_parse!( | |
| tag!("P") >> | |
| date: date_half >> | |
| time: opt!(time_half) >> | |
| (duration_from_parts(date, time.unwrap_or(Vec::new()))) | |
| ) | |
| ); | |
| fn duration_from_parts(date: Vec<(f32, DurationUnit)>, time: Vec<(f32, DurationUnit)>) -> Duration { | |
| let mut ret = Duration::new(); | |
| for (v, unit) in date.iter().chain(time.iter()) { | |
| match unit { | |
| DurationUnit::Years => ret.set_years(*v), | |
| DurationUnit::Months => ret.set_months(*v), | |
| DurationUnit::Weeks => ret.set_weeks(*v), | |
| DurationUnit::Days => ret.set_days(*v), | |
| DurationUnit::Hours => ret.set_hours(*v), | |
| DurationUnit::Minutes => ret.set_minutes(*v), | |
| DurationUnit::Seconds => ret.set_seconds(*v), | |
| } | |
| } | |
| ret | |
| } | |
| fn parse_value(s: &str) -> Result<f32, ::std::num::ParseFloatError> { | |
| println!("parsing value {:?}", s); | |
| s.parse().map_err(|e| { | |
| println!("failed to parse float: {:?} {}", s, e); | |
| e | |
| }) | |
| } | |
| // fn is_tag(c: char) -> bool { | |
| // c == 'Y' | |
| // || c == 'M' | |
| // || c == 'W' | |
| // || c == 'D' | |
| // || c == 'T' | |
| // || c == 'H' | |
| // || c == 'M' | |
| // || c == 'S' | |
| // } | |
| fn is_value_char(c: char) -> bool { | |
| c.is_digit(10) || c == '.' | |
| } | |
| named!(unit_value<&str, f32>, | |
| dbg!(map_res!(take_while!(is_value_char), parse_value)) | |
| ); | |
| named!(date_unit_tag<&str, DurationUnit>, | |
| map_res!(take!(1), parse_date_unit) | |
| ); | |
| named!(date_part<&str, (f32, DurationUnit)>, | |
| pair!(unit_value, date_unit_tag) | |
| ); | |
| named!(time_unit_tag<&str, DurationUnit>, | |
| map_res!(take!(1), parse_time_unit) | |
| ); | |
| named!(time_part<&str, (f32, DurationUnit)>, | |
| pair!(unit_value, time_unit_tag) | |
| ); | |
| named!(date_half<&str, Vec<(f32, DurationUnit)>>, | |
| fold_many0!(date_part, Vec::new(), |mut acc: Vec<_>, item| { | |
| acc.push(item); | |
| acc | |
| }) | |
| ); | |
| named!(time_half<&str, Vec<(f32, DurationUnit)>>, | |
| do_parse!( | |
| tag!("T") >> | |
| ret: fold_many0!(time_part, Vec::new(), |mut acc: Vec<_>, item| { | |
| acc.push(item); | |
| acc | |
| }) >> | |
| (ret) | |
| ) | |
| ); | |
| fn parse_date_unit(s: &str) -> Result<DurationUnit, String> { | |
| match s { | |
| "Y" => Ok(DurationUnit::Years), | |
| "M" => Ok(DurationUnit::Months), | |
| "W" => Ok(DurationUnit::Weeks), | |
| "D" => Ok(DurationUnit::Days), | |
| _ => { | |
| eprintln!("Error parsing time unit: {:?}", s); | |
| Err(format!("Unable to parser date portion of duration")) | |
| }, | |
| } | |
| } | |
| fn parse_time_unit(s: &str) -> Result<DurationUnit, String> { | |
| match s { | |
| "H" => Ok(DurationUnit::Hours), | |
| "M" => Ok(DurationUnit::Minutes), | |
| "S" => Ok(DurationUnit::Seconds), | |
| _ => { | |
| eprintln!("Error parsing time unit: {:?}", s); | |
| Err(format!("Unable to parse time portion of duration")) | |
| }, | |
| } | |
| } | |
| #[derive(Clone, Copy, Debug)] | |
| enum DurationUnit { | |
| Years, | |
| Months, | |
| Weeks, | |
| Days, | |
| Hours, | |
| Minutes, | |
| Seconds | |
| } | |
| fn main() { | |
| parse("P0Y").unwrap(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment