1
// Copyright 2020, Collabora Ltd.
2
// SPDX-License-Identifier: MIT OR Apache-2.0
3

            
4
// Structures used to poll the status
5

            
6
use std::time::Duration;
7

            
8
use reqwest::Client;
9
use serde::Deserialize;
10

            
11
use crate::ddi::cancel_action::CancelAction;
12
use crate::ddi::client::Error;
13
use crate::ddi::common::Link;
14
use crate::ddi::config_data::ConfigRequest;
15
use crate::ddi::confirmation_base::ConfirmationRequest;
16
use crate::ddi::deployment_base::UpdatePreFetch;
17

            
18
#[derive(Debug, Deserialize)]
19
pub(crate) struct ReplyInternal {
20
    config: Config,
21
    #[serde(rename = "_links")]
22
    links: Option<Links>,
23
}
24
#[derive(Debug, Deserialize)]
25
pub struct Config {
26
    polling: Polling,
27
}
28
#[derive(Debug, Deserialize)]
29
pub struct Polling {
30
    sleep: String,
31
}
32
#[derive(Debug, Deserialize)]
33
pub struct Links {
34
    #[serde(rename = "configData")]
35
    config_data: Option<Link>,
36
    #[serde(rename = "deploymentBase")]
37
    deployment_base: Option<Link>,
38
    #[serde(rename = "confirmationBase")]
39
    confirmation_base: Option<Link>,
40
    #[serde(rename = "cancelAction")]
41
    cancel_action: Option<Link>,
42
}
43

            
44
/// Polling reply from the server
45
#[derive(Debug)]
46
pub struct Reply {
47
    reply: ReplyInternal,
48
    client: Client,
49
}
50

            
51
impl Reply {
52
72
    pub(crate) fn new(reply: ReplyInternal, client: Client) -> Self {
53
72
        Self { reply, client }
54
72
    }
55

            
56
    /// Suggested sleeping time between two polling requests to the server.
57
8
    pub fn polling_sleep(&self) -> Result<Duration, Error> {
58
8
        self.reply.config.polling.as_duration()
59
8
    }
60

            
61
    /// Returns pending configuration data request from the server, if any.
62
36
    pub fn config_data_request(&self) -> Option<ConfigRequest> {
63
36
        match &self.reply.links {
64
36
            Some(links) => links
65
36
                .config_data
66
36
                .as_ref()
67
39
                .map(|l| ConfigRequest::new(self.client.clone(), l.to_string())),
68
            None => None,
69
        }
70
36
    }
71

            
72
    /// Returns pending update to deploy, if any.
73
48
    pub fn update(&self) -> Option<UpdatePreFetch> {
74
48
        match &self.reply.links {
75
48
            Some(links) => links
76
48
                .deployment_base
77
48
                .as_ref()
78
54
                .map(|l| UpdatePreFetch::new(self.client.clone(), l.to_string())),
79
            None => None,
80
        }
81
48
    }
82

            
83
    /// Returns pending confirmations, if any.
84
12
    pub fn confirmation_base(&self) -> Option<ConfirmationRequest> {
85
12
        match &self.reply.links {
86
12
            Some(links) => links
87
12
                .confirmation_base
88
12
                .as_ref()
89
15
                .map(|l| ConfirmationRequest::new(self.client.clone(), l.to_string())),
90
            None => None,
91
        }
92
12
    }
93

            
94
    /// Returns pending cancel action, if any.
95
4
    pub fn cancel_action(&self) -> Option<CancelAction> {
96
4
        match &self.reply.links {
97
4
            Some(links) => links
98
4
                .cancel_action
99
4
                .as_ref()
100
5
                .map(|l| CancelAction::new(self.client.clone(), l.to_string())),
101
            None => None,
102
        }
103
4
    }
104
}
105

            
106
impl Polling {
107
18
    fn as_duration(&self) -> Result<Duration, Error> {
108
55
        let times: Vec<Result<u64, _>> = self.sleep.split(':').map(|s| s.parse()).collect();
109
18
        if times.len() != 3 {
110
4
            return Err(Error::InvalidSleep);
111
14
        }
112

            
113
14
        match times[..] {
114
14
            [Ok(h), Ok(m), Ok(s)] => Ok(Duration::new(h * 60 * 60 + m * 60 + s, 0)),
115
            _ => Ok(Duration::new(0, 0)),
116
        }
117
18
    }
118
}
119

            
120
#[cfg(test)]
121
mod tests {
122
    use super::*;
123

            
124
    #[test]
125
2
    fn sleep_duration() {
126
2
        let polling = Polling {
127
2
            sleep: "00:00:05".to_string(),
128
2
        };
129
2
        assert_eq!(polling.as_duration().unwrap(), Duration::new(5, 0));
130

            
131
2
        let polling = Polling {
132
2
            sleep: "00:05:05".to_string(),
133
2
        };
134
2
        assert_eq!(polling.as_duration().unwrap(), Duration::new(305, 0));
135

            
136
2
        let polling = Polling {
137
2
            sleep: "01:05:05".to_string(),
138
2
        };
139
2
        assert_eq!(polling.as_duration().unwrap(), Duration::new(3905, 0));
140

            
141
2
        let polling = Polling {
142
2
            sleep: "05:05".to_string(),
143
2
        };
144
2
        assert!(polling.as_duration().is_err());
145

            
146
2
        let polling = Polling {
147
2
            sleep: "invalid".to_string(),
148
2
        };
149
2
        assert!(polling.as_duration().is_err());
150
2
    }
151
}