Parse simple dates and times
This commit is contained in:
parent
106111e7c6
commit
107966389c
4 changed files with 183 additions and 0 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
|
@ -27,6 +27,29 @@ version = "0.2.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.44"
|
version = "0.1.44"
|
||||||
|
|
@ -62,8 +85,15 @@ name = "today"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
|
nom = "7.1.0"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod parse;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
|
|
||||||
151
src/parse.rs
Normal file
151
src/parse.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use chrono::{NaiveDate, NaiveTime};
|
||||||
|
use nom::branch::Alt;
|
||||||
|
use nom::bytes::complete::{take_while1, take_while_m_n};
|
||||||
|
use nom::character::complete::{char, digit1, newline};
|
||||||
|
use nom::combinator::{eof, fail, map_opt, map_res};
|
||||||
|
use nom::sequence::terminated;
|
||||||
|
use nom::{IResult, Parser};
|
||||||
|
|
||||||
|
fn line_ending(i: &str) -> IResult<&str, ()> {
|
||||||
|
(newline.map(|_| ()), eof.map(|_| ())).choice(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn title(i: &str) -> IResult<&str, &str> {
|
||||||
|
terminated(take_while1(|c| c != '\n'), line_ending)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number<N: FromStr>(i: &str) -> IResult<&str, N> {
|
||||||
|
map_res(digit1, str::parse)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fixed_width_number<N: FromStr, const W: usize>(i: &str) -> IResult<&str, N> {
|
||||||
|
map_res(
|
||||||
|
take_while_m_n(W, W, |c: char| c.is_ascii_digit()),
|
||||||
|
str::parse,
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn date(i: &str) -> IResult<&str, NaiveDate> {
|
||||||
|
let (i, year) = number::<i32>(i)?;
|
||||||
|
let (i, _) = char('-')(i)?;
|
||||||
|
let (i, month) = fixed_width_number::<u32, 2>(i)?;
|
||||||
|
let (i, _) = char('-')(i)?;
|
||||||
|
let (i, day) = fixed_width_number::<u32, 2>(i)?;
|
||||||
|
match NaiveDate::from_ymd_opt(year, month, day) {
|
||||||
|
Some(date) => Ok((i, date)),
|
||||||
|
None => fail(i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn time(i: &str) -> IResult<&str, NaiveTime> {
|
||||||
|
let (i, hour) = fixed_width_number::<u32, 2>(i)?;
|
||||||
|
let (i, _) = char(':')(i)?;
|
||||||
|
let (i, min) = fixed_width_number::<u32, 2>(i)?;
|
||||||
|
if hour < 24 && min < 60 {
|
||||||
|
Ok((i, NaiveTime::from_hms(hour, min, 0)))
|
||||||
|
} else {
|
||||||
|
fail(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_line_ending() {
|
||||||
|
assert_eq!(line_ending("\n"), Ok(("", ())));
|
||||||
|
assert_eq!(line_ending("\nbla"), Ok(("bla", ())));
|
||||||
|
assert_eq!(line_ending("\n\n"), Ok(("\n", ())));
|
||||||
|
assert_eq!(line_ending(""), Ok(("", ())));
|
||||||
|
|
||||||
|
assert!(line_ending("bla").is_err());
|
||||||
|
assert!(line_ending("\r").is_err());
|
||||||
|
assert!(line_ending("\r\n").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_title() {
|
||||||
|
assert_eq!(title("foo bar\nbaz"), Ok(("baz", "foo bar")));
|
||||||
|
assert_eq!(title("foo bar\n"), Ok(("", "foo bar")));
|
||||||
|
assert_eq!(title("foo bar"), Ok(("", "foo bar")));
|
||||||
|
assert_eq!(title(" \nbla"), Ok(("bla", " ")));
|
||||||
|
assert_eq!(
|
||||||
|
title("!\"§$%&/()'<>[]{}_:;.,-=\nbla"),
|
||||||
|
Ok(("bla", "!\"§$%&/()'<>[]{}_:;.,-="))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(title("\nxyz").is_err());
|
||||||
|
assert!(title("").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_number() {
|
||||||
|
assert_eq!(number::<u32>("012345abc"), Ok(("abc", 12345)));
|
||||||
|
assert_eq!(number::<u8>("255"), Ok(("", 255)));
|
||||||
|
assert_eq!(number::<i64>("0x3f"), Ok(("x3f", 0)));
|
||||||
|
|
||||||
|
assert!(number::<u8>("256").is_err());
|
||||||
|
assert!(number::<i32>("xyz").is_err());
|
||||||
|
assert!(number::<i8>("").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fixed_width_number() {
|
||||||
|
assert_eq!(
|
||||||
|
fixed_width_number::<u32, 2>("012345abc"),
|
||||||
|
Ok(("2345abc", 1))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fixed_width_number::<u32, 3>("012345abc"),
|
||||||
|
Ok(("345abc", 12))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fixed_width_number::<u32, 6>("012345abc"),
|
||||||
|
Ok(("abc", 12345))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(fixed_width_number::<u8, 0>("012345abc").is_err());
|
||||||
|
assert!(fixed_width_number::<u8, 6>("012345abc").is_err());
|
||||||
|
assert!(fixed_width_number::<u8, 3>("14x").is_err());
|
||||||
|
assert!(fixed_width_number::<i8, 4>("").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date() {
|
||||||
|
assert_eq!(
|
||||||
|
date("2021-11-06"),
|
||||||
|
Ok(("", NaiveDate::from_ymd(2021, 11, 6)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
date("2021-11-0678"),
|
||||||
|
Ok(("78", NaiveDate::from_ymd(2021, 11, 6)))
|
||||||
|
);
|
||||||
|
assert_eq!(date("0-01-01"), Ok(("", NaiveDate::from_ymd(0, 1, 1))));
|
||||||
|
assert_eq!(
|
||||||
|
date("2020-02-29"),
|
||||||
|
Ok(("", NaiveDate::from_ymd(2020, 2, 29)))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(date("2021-11-6").is_err());
|
||||||
|
assert!(date("0000-00-00").is_err());
|
||||||
|
assert!(date("2021-02-29").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_time() {
|
||||||
|
assert_eq!(time("12:34"), Ok(("", NaiveTime::from_hms(12, 34, 0))));
|
||||||
|
assert_eq!(time("00:00"), Ok(("", NaiveTime::from_hms(0, 0, 0))));
|
||||||
|
assert_eq!(time("23:59"), Ok(("", NaiveTime::from_hms(23, 59, 0))));
|
||||||
|
assert_eq!(time("02:04:06"), Ok((":06", NaiveTime::from_hms(2, 4, 0))));
|
||||||
|
|
||||||
|
assert!(time("abc").is_err());
|
||||||
|
assert!(time("24:23").is_err());
|
||||||
|
assert!(time("12:60").is_err());
|
||||||
|
assert!(time("12-34").is_err());
|
||||||
|
assert!(time("2:34").is_err());
|
||||||
|
assert!(time("12:3").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue