acts/model/act/
timeout.rs

1use crate::{ActError, Result, Step};
2use regex::Regex;
3use serde::{Deserialize, Serialize, de};
4use std::str::FromStr;
5
6#[derive(Debug, Default, Clone, Serialize, Deserialize)]
7pub struct Timeout {
8    #[serde(default)]
9    pub on: String,
10    #[serde(default)]
11    pub steps: Vec<Step>,
12}
13
14#[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize, Clone)]
15pub enum TimeoutUnit {
16    #[default]
17    Second,
18    Minute,
19    Hour,
20    Day,
21}
22
23#[derive(Debug, Default, PartialEq, Clone)]
24pub struct TimeoutLimit {
25    pub value: i64,
26    pub unit: TimeoutUnit,
27}
28
29impl FromStr for TimeoutLimit {
30    type Err = ActError;
31    fn from_str(s: &str) -> Result<Self> {
32        let value = TimeoutLimit::parse(s)?;
33        Ok(value)
34    }
35}
36
37impl std::fmt::Display for TimeoutLimit {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        f.write_fmt(format_args!(
40            "{}{}",
41            self.value,
42            match self.unit {
43                TimeoutUnit::Second => "s",
44                TimeoutUnit::Minute => "m",
45                TimeoutUnit::Hour => "h",
46                TimeoutUnit::Day => "d",
47            }
48        ))
49    }
50}
51
52impl<'de> Deserialize<'de> for TimeoutLimit {
53    fn deserialize<D>(deserializer: D) -> std::prelude::v1::Result<Self, D::Error>
54    where
55        D: serde::Deserializer<'de>,
56    {
57        struct TimeoutVisitor;
58        impl de::Visitor<'_> for TimeoutVisitor {
59            type Value = TimeoutLimit;
60            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
61                formatter.write_str("step timeout")
62            }
63
64            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
65            where
66                E: de::Error,
67            {
68                TimeoutLimit::parse(v).map_err(|err| E::custom(err))
69            }
70        }
71        deserializer.deserialize_any(TimeoutVisitor)
72    }
73}
74
75impl Serialize for TimeoutLimit {
76    fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
77    where
78        S: serde::Serializer,
79    {
80        serializer.serialize_str(&self.to_string())
81    }
82}
83
84impl TimeoutLimit {
85    pub fn parse(expr: &str) -> Result<Self> {
86        let re = Regex::new(r"^(.*)(s|m|h|d)$").unwrap();
87        let caps = re.captures(expr);
88
89        if let Some(caps) = caps {
90            let value = caps.get(1).map_or("0", |m| m.as_str());
91            let unit = caps.get(2).map_or("s", |m| m.as_str());
92
93            let value = value
94                .parse::<i64>()
95                .map_err(|err| ActError::Model(format!("timeout parse error with '{err}'")))?;
96            let unit = TimeoutUnit::parse(unit)?;
97
98            return Ok(Self { value, unit });
99        }
100
101        Err(ActError::Model(format!(
102            "timeout parse error with '{expr}'"
103        )))
104    }
105
106    pub fn as_secs(&self) -> i64 {
107        match self.unit {
108            TimeoutUnit::Second => self.value,
109            TimeoutUnit::Minute => self.value * 60,
110            TimeoutUnit::Hour => self.value * 60 * 60,
111            TimeoutUnit::Day => self.value * 60 * 60 * 24,
112        }
113    }
114}
115
116impl TimeoutUnit {
117    fn parse(expr: &str) -> Result<Self> {
118        match expr {
119            "s" => Ok(TimeoutUnit::Second),
120            "m" => Ok(TimeoutUnit::Minute),
121            "h" => Ok(TimeoutUnit::Hour),
122            "d" => Ok(TimeoutUnit::Day),
123            _ => Err(ActError::Model(format!(
124                "timeout parse error with '{expr}'"
125            ))),
126        }
127    }
128}
129
130impl Timeout {
131    pub fn new() -> Self {
132        Self::default()
133    }
134    pub fn with_on(mut self, v: &str) -> Self {
135        // self.on = TimeoutLimit::parse(v)
136        //     .unwrap_or_else(|_| panic!("failed with error format '{v}' for 'on' "));
137        self.on = v.to_string();
138        self
139    }
140
141    pub fn with_step(mut self, build: fn(Step) -> Step) -> Self {
142        self.steps.push(build(Step::default()));
143
144        self
145    }
146}