Skip to main content

apollo_configuration/types/
duration.rs

1//! Support for [`std::time::Duration`] in configuration.
2
3use std::borrow::Cow;
4use std::fmt;
5use std::str::FromStr;
6
7use derive_more::{Deref, From};
8use humantime_serde::re::humantime;
9use schemars::JsonSchema;
10use serde::de::Deserializer;
11use serde::{Deserialize, Serialize};
12
13use crate::Validate;
14
15/// A duration parsed from human-readable format in configuration.
16///
17/// Supports formats like `30s`, `5m`, `1h30m`, `100ms`.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deref, From)]
19pub struct Duration(std::time::Duration);
20
21impl FromStr for Duration {
22    type Err = humantime::DurationError;
23
24    fn from_str(s: &str) -> Result<Self, Self::Err> {
25        humantime::parse_duration(s).map(Self)
26    }
27}
28
29impl fmt::Display for Duration {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "{}", humantime::format_duration(self.0))
32    }
33}
34
35impl<'de> Deserialize<'de> for Duration {
36    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37    where
38        D: Deserializer<'de>,
39    {
40        let s = String::deserialize(deserializer)?;
41        humantime::parse_duration(&s)
42            .map(Self)
43            .map_err(serde::de::Error::custom)
44    }
45}
46
47impl Serialize for Duration {
48    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49    where
50        S: serde::Serializer,
51    {
52        humantime_serde::serialize(&self.0, serializer)
53    }
54}
55
56impl JsonSchema for Duration {
57    fn schema_name() -> Cow<'static, str> {
58        Cow::Borrowed("Duration")
59    }
60
61    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
62        String::json_schema(generator)
63    }
64}
65
66impl Validate for Duration {}
67
68#[cfg(test)]
69mod tests {
70    use super::Duration;
71    use std::str::FromStr as _;
72
73    #[test]
74    fn parse_from_str() {
75        assert_eq!(
76            Duration::from_str("5m 20s")
77                .expect("should parse")
78                .as_secs(),
79            320
80        );
81        let _err = Duration::from_str("5q").expect_err("should not parse");
82    }
83}