1use crate::{
2 impl_expect_err, impl_expect_err_internal, CondUnit, DError, DResult, ExpectErr,
3 ONE_DAY_NANOSECOND, ONE_HOUR_NANOSECOND, ONE_MICROSECOND_NANOSECOND,
4 ONE_MILLISECOND_NANOSECOND, ONE_MINUTE_NANOSECOND, ONE_MONTH_NANOSECOND, ONE_SECOND_NANOSECOND,
5 ONE_WEEK_NANOSECOND, ONE_YEAR_NANOSECOND,
6};
7use std::fmt::{Debug, Display, Formatter};
8use std::str::FromStr;
9use winnow::ascii::multispace0;
10use winnow::combinator::{cut_err, eof, peek};
11use winnow::error::{ContextError, StrContext, StrContextValue};
12use winnow::stream::AsChar;
13use winnow::token::{one_of, take_while};
14use winnow::ModalResult as WResult;
15use winnow::Parser;
16
17#[derive(Debug, Eq, PartialEq, Default, Clone)]
18pub(crate) enum TimeUnit {
19 Year,
20 Month,
21 Week,
22 Day,
23 Hour,
24 Minute,
25 #[default]
26 Second,
27 MilliSecond,
28 MicroSecond,
29 NanoSecond,
30}
31
32impl Display for TimeUnit {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 match self {
35 TimeUnit::Year => write!(f, "y"),
36 TimeUnit::Month => write!(f, "mon"),
37 TimeUnit::Week => write!(f, "w"),
38 TimeUnit::Day => write!(f, "d"),
39 TimeUnit::Hour => write!(f, "h"),
40 TimeUnit::Minute => write!(f, "min"),
41 TimeUnit::Second => write!(f, "s"),
42 TimeUnit::MilliSecond => write!(f, "ms"),
43 TimeUnit::MicroSecond => write!(f, "µs"),
44 TimeUnit::NanoSecond => write!(f, "ns"),
45 }
46 }
47}
48
49impl TimeUnit {
50 #[inline]
51 #[cfg(feature = "cn_unit")]
52 fn is_cn_unit(c: char) -> bool {
53 [
54 '年', '月', '周', '日', '天', '时', '分', '秒', '毫', '微', '纳',
55 ]
56 .contains(&c)
57 }
58
59 pub(crate) fn duration(&self, time_str: impl AsRef<str>) -> DResult<u64> {
60 let time = time_str
61 .as_ref()
62 .parse::<u64>()
63 .map_err(|err| DError::ParseError(err.to_string()))?;
64 let unit = match self {
65 TimeUnit::Year => ONE_YEAR_NANOSECOND,
66 TimeUnit::Month => ONE_MONTH_NANOSECOND,
67 TimeUnit::Week => ONE_WEEK_NANOSECOND,
68 TimeUnit::Day => ONE_DAY_NANOSECOND,
69 TimeUnit::Hour => ONE_HOUR_NANOSECOND,
70 TimeUnit::Minute => ONE_MINUTE_NANOSECOND,
71 TimeUnit::Second => ONE_SECOND_NANOSECOND,
72 TimeUnit::MilliSecond => ONE_MILLISECOND_NANOSECOND,
73 TimeUnit::MicroSecond => ONE_MICROSECOND_NANOSECOND,
74 TimeUnit::NanoSecond => 1,
75 };
76 time.checked_mul(unit).ok_or(DError::OverflowError)
77 }
78}
79
80impl FromStr for TimeUnit {
81 type Err = DError;
82
83 #[inline(always)]
84 fn from_str(s: &str) -> Result<Self, Self::Err> {
85 let owned;
86 let case = if cfg!(feature = "lowercase") {
87 s
88 } else {
89 owned = s.to_ascii_lowercase();
90 owned.as_str()
91 };
92
93 match case {
94 "y" | "year" | "years" => Ok(TimeUnit::Year),
95 "mon" | "month" | "months" => Ok(TimeUnit::Month),
96 "w" | "week" | "weeks" => Ok(TimeUnit::Week),
97 "d" | "day" | "days" => Ok(TimeUnit::Day),
98 "h" | "hr" | "hour" | "hours" => Ok(TimeUnit::Hour),
99 "m" | "min" | "minute" | "minutes" => Ok(TimeUnit::Minute),
100 "s" | "sec" | "second" | "seconds" => Ok(TimeUnit::Second),
101 "ms" | "msec" | "millisecond" | "milliseconds" => Ok(TimeUnit::MilliSecond),
102 "µs" | "µsec" | "µsecond" | "us" | "usec" | "usecond" | "microsecond"
103 | "microseconds" => Ok(TimeUnit::MicroSecond),
104 "ns" | "nsec" | "nanosecond" | "nanoseconds" => Ok(TimeUnit::NanoSecond),
105
106 #[cfg(feature = "cn_unit")]
107 "年" => Ok(TimeUnit::Year),
108 #[cfg(feature = "cn_unit")]
109 "月" => Ok(TimeUnit::Month),
110 #[cfg(feature = "cn_unit")]
111 "周" => Ok(TimeUnit::Week),
112 #[cfg(feature = "cn_unit")]
113 "日" | "天" => Ok(TimeUnit::Day),
114 #[cfg(feature = "cn_unit")]
115 "时" => Ok(TimeUnit::Hour),
116 #[cfg(feature = "cn_unit")]
117 "分" => Ok(TimeUnit::Minute),
118 #[cfg(feature = "cn_unit")]
119 "秒" => Ok(TimeUnit::Second),
120 #[cfg(feature = "cn_unit")]
121 "毫秒" => Ok(TimeUnit::MilliSecond),
122 #[cfg(feature = "cn_unit")]
123 "微秒" => Ok(TimeUnit::MicroSecond),
124 #[cfg(feature = "cn_unit")]
125 "纳秒" => Ok(TimeUnit::NanoSecond),
126 _ => Err(DError::ParseError(Self::expect_err(case))),
127 }
128 }
129}
130
131impl_expect_err!(
132 TimeUnit,
133 [&'static str; 11],
134 ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]
135);
136
137pub(crate) fn unit_abbr1(input: &mut &str) -> WResult<TimeUnit> {
138 let set = |c: char| c.is_alpha() || c == 'µ';
139 let set = {
140 #[cfg(feature = "cn_unit")]
141 {
142 move |c: char| set(c) || TimeUnit::is_cn_unit(c)
143 }
144 #[cfg(not(feature = "cn_unit"))]
145 set
146 };
147
148 take_while(1.., set)
149 .parse_to()
150 .context(StrContext::Expected(StrContextValue::Description(
151 TimeUnit::get_expect_val(),
152 )))
153 .parse_next(input)
154}
155
156pub(crate) fn opt_unit_abbr(input: &mut &str) -> WResult<TimeUnit> {
157 let result = unit_abbr1(input);
158 if result.is_err() {
159 multispace0.parse_next(input)?;
160 if eof::<_, ContextError>.parse_next(input).is_ok() {
161 return Ok(TimeUnit::default());
163 }
164
165 return cut_err(peek(one_of(CondUnit::contain)))
166 .context(StrContext::Expected(StrContextValue::Description(
167 TimeUnit::get_expect_val(),
168 )))
169 .value(TimeUnit::default())
170 .parse_next(input);
171 }
172 result
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::catch_err;
179 use winnow::{Parser, Partial};
180
181 #[test]
182 fn test_time_unit_abbr() {
183 assert_eq!(
184 unit_abbr1.parse_peek(&Partial::new("y")),
185 Ok(("", TimeUnit::Year))
186 );
187 assert_eq!(
188 unit_abbr1.parse_peek(&Partial::new("mon")),
189 Ok(("", TimeUnit::Month))
190 );
191 assert_eq!(
192 unit_abbr1.parse_peek(&Partial::new("w")),
193 Ok(("", TimeUnit::Week))
194 );
195 assert_eq!(
196 unit_abbr1.parse_peek(&Partial::new("d")),
197 Ok(("", TimeUnit::Day))
198 );
199 assert_eq!(
200 unit_abbr1.parse_peek(&Partial::new("h")),
201 Ok(("", TimeUnit::Hour))
202 );
203 assert_eq!(
204 unit_abbr1.parse_peek(&Partial::new("m")),
205 Ok(("", TimeUnit::Minute))
206 );
207 assert_eq!(
208 unit_abbr1.parse_peek(&Partial::new("s")),
209 Ok(("", TimeUnit::Second))
210 );
211 assert_eq!(
212 unit_abbr1.parse_peek(&Partial::new("ms")),
213 Ok(("", TimeUnit::MilliSecond))
214 );
215 assert_eq!(
216 unit_abbr1.parse_peek(&Partial::new("µs")),
217 Ok(("", TimeUnit::MicroSecond))
218 );
219 assert_eq!(
220 unit_abbr1.parse_peek(&Partial::new("ns")),
221 Ok(("", TimeUnit::NanoSecond))
222 );
223 }
224
225 #[cfg(feature = "cn_unit")]
226 #[test]
227 fn test_time_cn_unit_abbr() {
228 assert_eq!(
229 unit_abbr1.parse_peek(&Partial::new("年")),
230 Ok(("", TimeUnit::Year))
231 );
232 assert_eq!(
233 unit_abbr1.parse_peek(&Partial::new("月")),
234 Ok(("", TimeUnit::Month))
235 );
236 assert_eq!(
237 unit_abbr1.parse_peek(&Partial::new("周")),
238 Ok(("", TimeUnit::Week))
239 );
240 assert_eq!(
241 unit_abbr1.parse_peek(&Partial::new("日")),
242 Ok(("", TimeUnit::Day))
243 );
244 assert_eq!(
245 unit_abbr1.parse_peek(&Partial::new("天")),
246 Ok(("", TimeUnit::Day))
247 );
248 assert_eq!(
249 unit_abbr1.parse_peek(&Partial::new("时")),
250 Ok(("", TimeUnit::Hour))
251 );
252 assert_eq!(
253 unit_abbr1.parse_peek(&Partial::new("分")),
254 Ok(("", TimeUnit::Minute))
255 );
256 assert_eq!(
257 unit_abbr1.parse_peek(&Partial::new("秒")),
258 Ok(("", TimeUnit::Second))
259 );
260 assert_eq!(
261 unit_abbr1.parse_peek(&Partial::new("毫秒")),
262 Ok(("", TimeUnit::MilliSecond))
263 );
264 assert_eq!(
265 unit_abbr1.parse_peek(&Partial::new("微秒")),
266 Ok(("", TimeUnit::MicroSecond))
267 );
268 assert_eq!(
269 unit_abbr1.parse_peek(&Partial::new("纳秒")),
270 Ok(("", TimeUnit::NanoSecond))
271 );
272 }
273
274 #[test]
275 fn test_time_unit() {
276 let (input, format) = unit_abbr1.parse_peek("m123").unwrap();
277 assert_eq!(input, "123");
278 assert_eq!(format, TimeUnit::Minute);
279 }
280
281 #[test]
282 fn test_unit_abbr1_err() {
283 let expect_err = r#"
284nys
285^
286expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#;
287 assert_eq!(
288 catch_err!(unit_abbr1.parse(&Partial::new("nys"))),
289 expect_err.trim_start()
290 );
291
292 let expect_err = r#"
293^
294expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#;
295 assert_eq!(catch_err!(unit_abbr1.parse(&Partial::new(""))), expect_err);
296 }
297
298 #[test]
299 fn test_opt_unit_abbr() {
300 assert_eq!(
301 opt_unit_abbr.parse_peek(&Partial::new("y")),
302 Ok(("", TimeUnit::Year))
303 );
304 assert_eq!(
305 opt_unit_abbr.parse_peek(&Partial::new("mon")),
306 Ok(("", TimeUnit::Month))
307 );
308 assert_eq!(
309 opt_unit_abbr.parse_peek(&Partial::new("w")),
310 Ok(("", TimeUnit::Week))
311 );
312 assert_eq!(
313 opt_unit_abbr.parse_peek(&Partial::new("d")),
314 Ok(("", TimeUnit::Day))
315 );
316 assert_eq!(
317 opt_unit_abbr.parse_peek(&Partial::new("h")),
318 Ok(("", TimeUnit::Hour))
319 );
320 assert_eq!(
321 opt_unit_abbr.parse_peek(&Partial::new("m")),
322 Ok(("", TimeUnit::Minute))
323 );
324 assert_eq!(
325 opt_unit_abbr.parse_peek(&Partial::new("s")),
326 Ok(("", TimeUnit::Second))
327 );
328 assert_eq!(
329 opt_unit_abbr.parse_peek(&Partial::new("ms")),
330 Ok(("", TimeUnit::MilliSecond))
331 );
332 assert_eq!(
333 opt_unit_abbr.parse_peek(&Partial::new("µs")),
334 Ok(("", TimeUnit::MicroSecond))
335 );
336 assert_eq!(
337 opt_unit_abbr.parse_peek(&Partial::new("ns")),
338 Ok(("", TimeUnit::NanoSecond))
339 );
340
341 assert_eq!(
342 opt_unit_abbr.parse_peek(&Partial::new("")),
343 Ok(("", TimeUnit::Second))
344 );
345
346 assert_eq!(
347 opt_unit_abbr.parse_peek(&Partial::new(" ")),
348 Ok(("", TimeUnit::Second))
349 );
350 }
351
352 #[test]
353 fn test_opt_unit_abbr_err() {
354 let expect_err = r#"
355nys
356^
357expected ["y", "mon", "w", "d", "h", "m", "s", "ms", "µs", "us", "ns"]"#;
358 assert_eq!(
359 catch_err!(opt_unit_abbr.parse(&Partial::new("nys"))),
360 expect_err.trim_start()
361 );
362 }
363}