extern crate combine;
use combine::*;
use combine::combinator::FnParser;
use combine::primitives::Stream;
#[derive(PartialEq, Debug)]
pub struct Date {
pub year: i32,
pub month: i32,
pub day: i32
}
#[derive(PartialEq, Debug)]
pub struct Time {
pub hour: i32,
pub minute: i32,
pub second: i32,
pub time_zone: i32
}
#[derive(PartialEq, Debug)]
pub struct DateTime {
pub date: Date,
pub time: Time
}
fn two_digits_to_int((x, y): (char, char)) -> i32 {
let x = x.to_digit(10).expect("digit");
let y = y.to_digit(10).expect("digit");
(x * 10 + y) as i32
}
fn two_digits<I>() -> FnParser<I, fn (State<I>) -> ParseResult<i32, I>>
where I: Stream<Item=char> {
fn two_digits_<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char> {
(digit(), digit())
.map(two_digits_to_int)
.parse_state(input)
}
parser(two_digits_)
}
fn time_zone<I>(input: State<I>) -> ParseResult<i32, I>
where I: Stream<Item=char> {
let utc = char('Z').map(|_| 0);
let offset = (
choice([char('-'), char('+')]),
two_digits(),
optional(optional(char(':')).with(two_digits()))
)
.map(|(sign, hour, minute)| {
let offset = hour * 60 + minute.unwrap_or(0);
if sign == '-' { -offset }
else { offset }
});
utc.or(offset)
.parse_state(input)
}
fn date<I>(input: State<I>) -> ParseResult<Date, I>
where I: Stream<Item=char> {
(many::<String, _>(digit()), char('-'), two_digits(), char('-'), two_digits())
.map(|(year, _, month, _, day)| {
Date {
year: year.parse().unwrap(),
month: month,
day: day
}
})
.parse_state(input)
}
fn time<I>(input: State<I>) -> ParseResult<Time, I>
where I: Stream<Item=char> {
(two_digits(), char(':'), two_digits(), char(':'), two_digits(), parser(time_zone))
.map(|(hour, _, minute, _, second, time_zone)| {
Time {
hour: hour,
minute: minute,
second: second,
time_zone: time_zone
}
})
.parse_state(input)
}
fn date_time<I>(input: State<I>) -> ParseResult<DateTime, I>
where I: Stream<Item=char> {
(parser(date), char('T'), parser(time))
.map(|(date, _, time)| {
DateTime {
date: date,
time: time
}
})
.parse_state(input)
}
#[test]
fn test() {
let result = parser(date_time).parse("2015-08-02T18:54:42+02");
let d = DateTime {
date: Date { year: 2015, month: 8, day: 2 },
time: Time { hour: 18, minute: 54, second: 42, time_zone: 2 * 60 },
};
assert_eq!(result, Ok((d, "")));
let result = parser(date_time).parse("50015-12-30T08:54:42Z");
let d = DateTime {
date: Date { year: 50015, month: 12, day: 30 },
time: Time { hour: 8, minute: 54, second: 42, time_zone: 0 },
};
assert_eq!(result, Ok((d, "")));
}