Skip to content

Instantly share code, notes, and snippets.

@FreeMasen
Created November 6, 2018 19:34
Show Gist options
  • Save FreeMasen/3382db944429066a9593519b6f30f39c to your computer and use it in GitHub Desktop.
Save FreeMasen/3382db944429066a9593519b6f30f39c to your computer and use it in GitHub Desktop.
#[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