1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use nom::{
character::complete::{char, digit1},
combinator::{map_res, verify},
sequence::preceded,
IResult,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Date {
year: u16,
month_of_year: u8,
day_of_month: u8,
}
impl Date {
#[cfg(test)]
pub(crate) fn new(year: u16, month_of_year: u8, day_of_month: u8) -> Self {
Self {
year,
month_of_year,
day_of_month,
}
}
#[must_use]
pub fn year(&self) -> u16 {
self.year
}
#[must_use]
pub fn month_of_year(&self) -> u8 {
self.month_of_year
}
#[must_use]
pub fn day_of_month(&self) -> u8 {
self.day_of_month
}
}
pub(super) fn date(input: &str) -> IResult<&str, Date> {
let (input, year) = year(input)?;
let (input, month_of_year) = preceded(char('-'), month)(input)?;
let (input, day_of_month) = preceded(char('-'), day)(input)?;
Ok((
input,
Date {
year,
month_of_year,
day_of_month,
},
))
}
fn year(input: &str) -> IResult<&str, u16> {
verify(map_res(digit1, str::parse), |y| *y > 0)(input)
}
fn month(input: &str) -> IResult<&str, u8> {
verify(map_res(digit1, str::parse), |m| *m > 0 && *m <= 12)(input)
}
fn day(input: &str) -> IResult<&str, u8> {
verify(map_res(digit1, str::parse), |d| *d > 0 && *d <= 31)(input)
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[test]
fn valid_date() {
assert_eq!(
date("2022-08-15"),
Ok((
"",
Date {
year: 2022,
month_of_year: 8,
day_of_month: 15
}
)),
);
}
#[rstest]
fn invalid_date(
#[values(
"hello",
"0-1-1",
"2000-00-12",
"2000-13-12",
"2000-11-00",
"2000-11-32"
)]
input: &str,
) {
assert!(date(input).is_err());
}
}