actix_settings/settings/
timeout.rs

1use std::fmt;
2
3use once_cell::sync::Lazy;
4use regex::Regex;
5use serde::de;
6
7use crate::{AsResult, Error, Parse};
8
9/// A timeout duration in milliseconds or seconds.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum Timeout {
12    /// The default timeout. Depends on context.
13    Default,
14
15    /// Timeout in milliseconds.
16    Milliseconds(usize),
17
18    /// Timeout in seconds.
19    Seconds(usize),
20}
21
22impl Parse for Timeout {
23    fn parse(string: &str) -> AsResult<Self> {
24        pub static FMT: Lazy<Regex> = Lazy::new(|| {
25            Regex::new(r"^\d+ (milliseconds|seconds)$").expect("Failed to compile regex: FMT")
26        });
27
28        pub static DIGITS: Lazy<Regex> =
29            Lazy::new(|| Regex::new(r"^\d+").expect("Failed to compile regex: DIGITS"));
30
31        pub static UNIT: Lazy<Regex> = Lazy::new(|| {
32            Regex::new(r"(milliseconds|seconds)$").expect("Failed to compile regex: UNIT")
33        });
34
35        macro_rules! invalid_value {
36            ($got:expr) => {
37                Err(InvalidValue! {
38                    expected: "a string of the format \"N seconds\" or \"N milliseconds\" where N is an integer > 0",
39                    got: $got,
40                })
41            }
42        }
43
44        match string {
45            "default" => Ok(Timeout::Default),
46
47            string if !FMT.is_match(string) => invalid_value!(string),
48
49            string => match (DIGITS.find(string), UNIT.find(string)) {
50                (None, _) | (_, None) => invalid_value!(string),
51
52                (Some(digits), Some(unit)) => {
53                    let digits = &string[digits.range()];
54                    let unit = &string[unit.range()];
55
56                    match (digits.parse(), unit) {
57                        (Ok(n), "milliseconds") => Ok(Timeout::Milliseconds(n)),
58                        (Ok(n), "seconds") => Ok(Timeout::Seconds(n)),
59                        _ => invalid_value!(string),
60                    }
61                }
62            },
63        }
64    }
65}
66
67impl<'de> de::Deserialize<'de> for Timeout {
68    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69    where
70        D: de::Deserializer<'de>,
71    {
72        struct TimeoutVisitor;
73
74        impl de::Visitor<'_> for TimeoutVisitor {
75            type Value = Timeout;
76
77            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78                let msg = "Either \"default\", \"disabled\", \"os\", or a string of the format \"N seconds\" where N is an integer > 0";
79                f.write_str(msg)
80            }
81
82            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
83            where
84                E: de::Error,
85            {
86                match Timeout::parse(value) {
87                    Ok(num_workers) => Ok(num_workers),
88                    Err(Error::InvalidValue { expected, got, .. }) => Err(
89                        de::Error::invalid_value(de::Unexpected::Str(&got), &expected),
90                    ),
91                    Err(_) => unreachable!(),
92                }
93            }
94        }
95
96        deserializer.deserialize_string(TimeoutVisitor)
97    }
98}