duration_str/
lib.rs

1#![doc(
2    html_logo_url = "https://raw.githubusercontent.com/baoyachi/duration-str/master/duration-str.png"
3)]
4//! Parse string to `Duration` .
5//!
6//! The String value unit support for one of:["y","mon","w","d","h","m","s", "ms", "µs", "ns"]
7//!
8//! - y:Year. Support string value: ["y" | "year" | "Y" | "YEAR" | "Year"]. e.g. 1y
9//!
10//! - mon:Month.Support string value: ["mon" | "MON" | "Month" | "month" | "MONTH"]. e.g. 1mon
11//!
12//! - w:Week.Support string value: ["w" | "W" | "Week" | "WEEK" | "week"]. e.g. 1w
13//!
14//! - d:Day.Support string value: ["d" | "D" | "Day" | "DAY" | "day"]. e.g. 1d
15//!
16//! - h:Hour.Support string value: ["h" | "H" | "hr" | "Hour" | "HOUR" | "hour"]. e.g. 1h
17//!
18//! - m:Minute.Support string value: ["m" | "M" | "Minute" | "MINUTE" | "minute" | "min" | "MIN"]. e.g. 1m
19//!
20//! - s:Second.Support string value: ["s" | "S" | "Second" | "SECOND" | "second" | "sec" | "SEC"]. e.g. 1s
21//!
22//! - ms:Millisecond.Support string value: ["ms" | "MS" | "Millisecond" | "MilliSecond" | "MILLISECOND" | "millisecond" | "mSEC" ]. e.g. 1ms
23//!
24//! - µs:Microsecond.Support string value: ["µs" | "µS" | "µsecond" | "us" | "uS" | "usecond" | "Microsecond" | "MicroSecond" | "MICROSECOND" | "microsecond" | "µSEC"]. e.g. 1µs
25//!
26//! - ns:Nanosecond.Support string value: ["ns" | "NS" | "Nanosecond" | "NanoSecond" | "NANOSECOND" | "nanosecond" | "nSEC"]. e.g. 1ns
27//!
28//! Also, `duration_str` support time duration simple evaluation(+,*). See examples below.
29//!
30//! # Example
31//! ```rust
32//! use duration_str::parse;
33//! use std::time::Duration;
34//!
35//! let duration = parse("1d").unwrap();
36//! assert_eq!(duration, Duration::new(24 * 60 * 60, 0));
37//!
38//! let duration = parse("3m+31").unwrap(); //the default duration unit is second.
39//! assert_eq!(duration, Duration::new(211, 0));
40//!
41//! let duration = parse("3m + 31").unwrap(); //the default duration unit is second.
42//! assert_eq!(duration, Duration::new(211, 0));
43//!
44//! let duration = parse("3m + 13s + 29ms").unwrap();
45//! assert_eq!(duration, Duration::new(193, 29 * 1000 * 1000 + 0 + 0));
46//!
47//! let duration = parse("3m + 1s + 29ms +17µs").unwrap();
48//! assert_eq!(
49//!     duration,
50//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
51//! );
52//!
53//! let duration = parse("3m 1s 29ms 17µs").unwrap();
54//! assert_eq!(
55//!     duration,
56//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
57//! );
58//!
59//! let duration = parse("3m1s29ms17us").unwrap();
60//! assert_eq!(
61//!     duration,
62//!     Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
63//! );
64//!
65//! let duration = parse("1m*10").unwrap(); //the default duration unit is second.
66//! assert_eq!(duration, Duration::new(600, 0));
67//!
68//! let duration = parse("1m*10ms").unwrap();
69//! assert_eq!(duration, Duration::new(0, 600 * 1000 * 1000));
70//!
71//! let duration = parse("1m * 1ns").unwrap();
72//! assert_eq!(duration, Duration::new(0, 60));
73//!
74//! let duration = parse("1m * 1m").unwrap();
75//! assert_eq!(duration, Duration::new(3600, 0));
76//! let duration = parse("42µs").unwrap();
77//! assert_eq!(duration,Duration::from_micros(42));
78//! ```
79//!
80//! # deserialize to std::time::Duration
81//!
82#![cfg_attr(not(feature = "serde"), doc = "This requires the `serde` feature")]
83//!
84#![cfg_attr(not(feature = "serde"), doc = "```ignore")]
85#![cfg_attr(feature = "serde", doc = "```rust")]
86//! use duration_str::deserialize_duration;
87//! use serde::*;
88//! use std::time::Duration;
89//!
90//! /// Uses `deserialize_duration`.
91//! #[derive(Debug, Deserialize)]
92//! struct Config {
93//!     #[serde(deserialize_with = "deserialize_duration")]
94//!     time_ticker: Duration,
95//! }
96//!
97//! fn needless_main() {
98//!     let json = r#"{"time_ticker":"1m+30"}"#;
99//!     let config: Config = serde_json::from_str(json).unwrap();
100//!     assert_eq!(config.time_ticker, Duration::new(60 + 30, 0));
101//!
102//!     let json = r#"{"time_ticker":"1m+30s"}"#;
103//!     let config: Config = serde_json::from_str(json).unwrap();
104//!     assert_eq!(config.time_ticker, Duration::new(60 + 30, 0));
105//!
106//!     let json = r#"{"time_ticker":"3m 1s 29ms 17µs"}"#;
107//!     let config: Config = serde_json::from_str(json).unwrap();
108//!     assert_eq!(
109//!         config.time_ticker,
110//!         Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
111//!     );
112//!
113//!     let json = r#"{"time_ticker":"3m1s29ms17us"}"#;
114//!     let config: Config = serde_json::from_str(json).unwrap();
115//!     assert_eq!(
116//!         config.time_ticker,
117//!         Duration::new(181, 29 * 1000 * 1000 + 17 * 1000 + 0)
118//!     );
119//! }
120//! ```
121//!
122//! # deserialize to chrono::Duration
123#![cfg_attr(
124    not(all(feature = "chrono", feature = "serde")),
125    doc = "This requires both the `chrono` and `serde` features"
126)]
127//!
128#![cfg_attr(not(all(feature = "chrono", feature = "serde")), doc = "```ignore")]
129#![cfg_attr(all(feature = "chrono", feature = "serde"), doc = "```rust")]
130//! use chrono::Duration;
131//! use duration_str::deserialize_duration_chrono;
132//! use serde::*;
133//!
134//! #[derive(Debug, Deserialize)]
135//! struct Config {
136//!     #[serde(deserialize_with = "deserialize_duration_chrono")]
137//!     time_ticker: Duration,
138//! }
139//!
140//! fn needless_main() {
141//!     let json = r#"{"time_ticker":"1m+30"}"#;
142//!     let config: Config = serde_json::from_str(json).unwrap();
143//!     assert_eq!(config.time_ticker, Duration::seconds(60 + 30));
144//!
145//!     let json = r#"{"time_ticker":"1m+30s"}"#;
146//!     let config: Config = serde_json::from_str(json).unwrap();
147//!     assert_eq!(config.time_ticker, Duration::seconds(60 + 30));
148//!
149//!     let json = r#"{"time_ticker":"3m 1s 29ms 17µs"}"#;
150//!     let config: Config = serde_json::from_str(json).unwrap();
151//!     assert_eq!(
152//!         config.time_ticker,
153//!         Duration::minutes(3)
154//!             + Duration::seconds(1)
155//!             + Duration::milliseconds(29)
156//!             + Duration::microseconds(17)
157//!     );
158//!
159//!     let json = r#"{"time_ticker":"3m1s29ms17us"}"#;
160//!     let config: Config = serde_json::from_str(json).unwrap();
161//!     assert_eq!(
162//!         config.time_ticker,
163//!         Duration::minutes(3)
164//!             + Duration::seconds(1)
165//!             + Duration::milliseconds(29)
166//!             + Duration::microseconds(17)
167//!     );
168//! }
169//! ```
170
171mod error;
172pub(crate) mod ext;
173pub(crate) mod macros;
174mod parser;
175#[cfg(feature = "serde")]
176mod serde;
177mod unit;
178
179pub use parser::parse;
180#[cfg(feature = "serde")]
181pub use serde::*;
182use std::fmt::{Debug, Display};
183
184use rust_decimal::prelude::ToPrimitive;
185use rust_decimal::Decimal;
186use std::str::FromStr;
187use std::time::Duration;
188
189pub use crate::error::DError;
190use crate::unit::TimeUnit;
191#[cfg(feature = "chrono")]
192pub use naive_date::{
193    after_naive_date, after_naive_date_time, before_naive_date, before_naive_date_time,
194};
195
196pub use ext::*;
197
198pub type DResult<T> = Result<T, DError>;
199
200const ONE_MICROSECOND_NANOSECOND: u64 = 1000;
201const ONE_MILLISECOND_NANOSECOND: u64 = 1000 * ONE_MICROSECOND_NANOSECOND;
202const ONE_SECOND_NANOSECOND: u64 = 1000 * ONE_MILLISECOND_NANOSECOND;
203const ONE_MINUTE_NANOSECOND: u64 = 60 * ONE_SECOND_NANOSECOND;
204const ONE_HOUR_NANOSECOND: u64 = 60 * ONE_MINUTE_NANOSECOND;
205const ONE_DAY_NANOSECOND: u64 = 24 * ONE_HOUR_NANOSECOND;
206const ONE_WEEK_NANOSECOND: u64 = 7 * ONE_DAY_NANOSECOND;
207const ONE_MONTH_NANOSECOND: u64 = 30 * ONE_DAY_NANOSECOND;
208const ONE_YEAR_NANOSECOND: u64 = 365 * ONE_DAY_NANOSECOND;
209
210// const ONE_SECOND_DECIMAL: Decimal = 1_000_000_000.into();
211fn one_second_decimal() -> Decimal {
212    1_000_000_000.into()
213}
214
215const PLUS: &str = "+";
216const STAR: &str = "*";
217
218trait ExpectErr {
219    type Output: Debug;
220
221    fn expect_val() -> Self::Output;
222
223    fn get_expect_val() -> &'static str;
224    fn expect_err<S: AsRef<str> + Display>(s: S) -> String;
225}
226
227#[macro_export]
228macro_rules! impl_expect_err {
229    ($type:ty, $output:ty, [$($val:tt),* $(,)?]) => {
230        impl ExpectErr for $type {
231            type Output = $output;
232
233            fn expect_val() -> Self::Output {
234                [$($val),*]
235            }
236
237            fn expect_err<S: AsRef<str> + Display>(s: S) -> String {
238                format!("expect one of:{:?}, but find:{}", Self::expect_val(), s)
239            }
240
241            fn get_expect_val() -> &'static str {
242                static EXPECT_VAL_STR: &str = concat!(
243                    "[",
244                    impl_expect_err_internal!($($val),*),
245                    "]"
246                );
247                EXPECT_VAL_STR
248            }
249        }
250    };
251}
252
253#[macro_export]
254macro_rules! impl_expect_err_internal {
255    // match empty
256    () => {
257        ""
258    };
259    // match single type
260    ($first:expr) => {
261        stringify!($first)
262    };
263    // match multi type
264    ($first:expr, $($rest:expr),*) => {
265        concat!(
266            stringify!($first),
267            ", ",
268            impl_expect_err_internal!($($rest),*)
269        )
270    };
271}
272
273#[derive(Debug, Eq, PartialEq, Clone)]
274enum CondUnit {
275    Plus,
276    Star,
277}
278
279impl FromStr for CondUnit {
280    type Err = String;
281
282    fn from_str(s: &str) -> Result<Self, Self::Err> {
283        match s {
284            "+" => Ok(CondUnit::Plus),
285            "*" => Ok(CondUnit::Star),
286            _ => Err(Self::expect_err(s)),
287        }
288    }
289}
290
291impl_expect_err!(CondUnit, [char; 2], ['+', '*']);
292
293impl CondUnit {
294    fn init() -> (Self, u64) {
295        (CondUnit::Star, ONE_SECOND_NANOSECOND)
296    }
297
298    fn contain(c: char) -> bool {
299        Self::expect_val().contains(&c)
300    }
301
302    fn change_duration(&self) -> u64 {
303        match self {
304            CondUnit::Plus => 0,
305            CondUnit::Star => ONE_SECOND_NANOSECOND,
306        }
307    }
308
309    fn calc(&self, x: u64, y: u64) -> DResult<Duration> {
310        let nano_second = match self {
311            CondUnit::Plus => x.checked_add(y).ok_or(DError::OverflowError)?,
312            CondUnit::Star => {
313                let x: Decimal = x.into();
314                let y: Decimal = y.into();
315                let ret = (x / one_second_decimal())
316                    .checked_mul(y / one_second_decimal())
317                    .ok_or(DError::OverflowError)?
318                    .checked_mul(one_second_decimal())
319                    .ok_or(DError::OverflowError)?;
320                ret.to_u64().ok_or(DError::OverflowError)?
321            }
322        };
323        Ok(Duration::from_nanos(nano_second))
324    }
325}
326
327trait Calc<T> {
328    fn calc(&self) -> DResult<T>;
329}
330
331impl Calc<(CondUnit, u64)> for Vec<(&str, CondUnit, TimeUnit)> {
332    fn calc(&self) -> DResult<(CondUnit, u64)> {
333        let (mut init_cond, mut init_duration) = CondUnit::init();
334        for (index, (val, cond, time_unit)) in self.iter().enumerate() {
335            if index == 0 {
336                init_cond = cond.clone();
337                init_duration = init_cond.change_duration();
338            } else if &init_cond != cond {
339                return Err(DError::ParseError(format!(
340                    "not support '{}' with '{}' calculate",
341                    init_cond, cond
342                )));
343            }
344            match init_cond {
345                CondUnit::Plus => {
346                    init_duration = init_duration
347                        .checked_add(time_unit.duration(val)?)
348                        .ok_or(DError::OverflowError)?;
349                }
350                CondUnit::Star => {
351                    let time: Decimal = time_unit.duration(val)?.into();
352                    let i = time / one_second_decimal();
353                    let mut init: Decimal = init_duration.into();
354                    init = init.checked_mul(i).ok_or(DError::OverflowError)?;
355                    init_duration = init.to_u64().ok_or(DError::OverflowError)?;
356                }
357            }
358        }
359        Ok((init_cond, init_duration))
360    }
361}
362
363impl Display for CondUnit {
364    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365        let str = match self {
366            Self::Plus => PLUS.to_string(),
367            Self::Star => STAR.to_string(),
368        };
369        write!(f, "{}", str)
370    }
371}
372
373/// convert `Into<String>` to `std::time::Duration`
374///
375/// # Example
376///
377/// ```rust
378/// use duration_str::parse;
379/// use std::time::Duration;
380///
381/// // supports units
382/// let duration = parse("1d").unwrap();
383/// assert_eq!(duration,Duration::new(24*60*60,0));
384///
385/// // supports addition
386/// let duration = parse("3m+31").unwrap();
387/// assert_eq!(duration,Duration::new(211,0));
388///
389/// // spaces are optional
390/// let duration = parse("3m + 31").unwrap();
391/// assert_eq!(duration,Duration::new(211,0));
392///
393/// // plus sign is optional
394/// let duration = parse("3m  31").unwrap();
395/// assert_eq!(duration,Duration::new(211,0));
396///
397/// // both plus and spaces are optional
398/// let duration = parse("3m31").unwrap();
399/// assert_eq!(duration,Duration::new(211,0));
400///
401/// // supports multiplication
402/// let duration = parse("1m*10").unwrap();
403/// assert_eq!(duration,Duration::new(600,0));
404///
405/// // spaces are optional
406/// let duration = parse("1m * 10").unwrap();
407/// assert_eq!(duration,Duration::new(600,0));
408/// ```
409pub fn parse_std(input: impl AsRef<str>) -> Result<Duration, String> {
410    parse(input.as_ref())
411}
412
413/// convert `Into<String>` to `chrono::Duration`
414///
415/// # Example
416///
417/// ```rust
418/// use duration_str::parse_chrono;
419/// use chrono::Duration;
420///
421/// // supports units
422/// let duration = parse_chrono("1d").unwrap();
423/// assert_eq!(duration,Duration::seconds(24*60*60));
424///
425/// // supports addition
426/// let duration = parse_chrono("3m+31").unwrap();
427/// assert_eq!(duration,Duration::seconds(211));
428///
429/// // spaces are optional
430/// let duration = parse_chrono("3m + 31").unwrap();
431/// assert_eq!(duration,Duration::seconds(211));
432///
433/// // plus sign is optional
434/// let duration = parse_chrono("3m  31").unwrap();
435/// assert_eq!(duration,Duration::seconds(211));
436///
437/// // both plus and spaces are optional
438/// let duration = parse_chrono("3m31").unwrap();
439/// assert_eq!(duration,Duration::seconds(211));
440///
441/// // supports multiplication
442/// let duration = parse_chrono("1m*10").unwrap();
443/// assert_eq!(duration,Duration::seconds(600));
444///
445/// // spaces are optional
446/// let duration = parse_chrono("1m * 10").unwrap();
447/// assert_eq!(duration,Duration::seconds(600));
448/// ```
449#[cfg(feature = "chrono")]
450pub fn parse_chrono(input: impl AsRef<str>) -> Result<chrono::Duration, String> {
451    let std_duration = parse_std(input)?;
452    let duration = chrono::Duration::from_std(std_duration).map_err(|e| e.to_string())?;
453    Ok(duration)
454}
455
456/// convert `Into<String>` to `time::Duration`
457///
458/// # Example
459///
460/// ```rust
461/// use duration_str::parse_time;
462/// use time::Duration;
463///
464/// // supports units
465/// let duration = parse_time("1d").unwrap();
466/// assert_eq!(duration,Duration::seconds(24*60*60));
467///
468/// // supports addition
469/// let duration = parse_time("3m+31").unwrap();
470/// assert_eq!(duration,Duration::seconds(211));
471///
472/// // spaces are optional
473/// let duration = parse_time("3m + 31").unwrap();
474/// assert_eq!(duration,Duration::seconds(211));
475///
476/// // plus sign is optional
477/// let duration = parse_time("3m  31").unwrap();
478/// assert_eq!(duration,Duration::seconds(211));
479///
480/// // both plus and spaces are optional
481/// let duration = parse_time("3m31").unwrap();
482/// assert_eq!(duration,Duration::seconds(211));
483///
484/// // supports multiplication
485/// let duration = parse_time("1m*10").unwrap();
486/// assert_eq!(duration,Duration::seconds(600));
487///
488/// // spaces are optional
489/// let duration = parse_time("1m * 10").unwrap();
490/// assert_eq!(duration,Duration::seconds(600));
491/// ```
492#[cfg(feature = "time")]
493pub fn parse_time(input: impl AsRef<str>) -> Result<time::Duration, String> {
494    let std_duration = parse_std(input)?;
495    let duration = time::Duration::try_from(std_duration).map_err(|e| e.to_string())?;
496    Ok(duration)
497}
498
499#[cfg(feature = "chrono")]
500mod naive_date {
501    use crate::parse_chrono;
502    use chrono::Utc;
503
504    #[allow(dead_code)]
505    pub enum TimeHistory {
506        Before,
507        After,
508    }
509
510    #[cfg(feature = "chrono")]
511    pub fn calc_naive_date_time(
512        input: impl AsRef<str>,
513        history: TimeHistory,
514    ) -> Result<chrono::NaiveDateTime, String> {
515        let duration = parse_chrono(input)?;
516        let time = match history {
517            TimeHistory::Before => (Utc::now() - duration).naive_utc(),
518            TimeHistory::After => (Utc::now() + duration).naive_utc(),
519        };
520        Ok(time)
521    }
522
523    macro_rules! gen_naive_date_func {
524        ($date_time:ident,$date:ident,$history:expr) => {
525            #[allow(dead_code)]
526            #[cfg(feature = "chrono")]
527            pub fn $date_time(input: impl AsRef<str>) -> Result<chrono::NaiveDateTime, String> {
528                calc_naive_date_time(input, $history)
529            }
530
531            #[allow(dead_code)]
532            #[cfg(feature = "chrono")]
533            pub fn $date(input: impl AsRef<str>) -> Result<chrono::NaiveDate, String> {
534                let date: chrono::NaiveDateTime = calc_naive_date_time(input, $history)?;
535                Ok(date.date())
536            }
537        };
538    }
539
540    gen_naive_date_func!(
541        before_naive_date_time,
542        before_naive_date,
543        TimeHistory::Before
544    );
545
546    gen_naive_date_func!(after_naive_date_time, after_naive_date, TimeHistory::After);
547}