1
// Copyright 2025, Liebherr Digital Development Center GmbH.
2
// SPDX-License-Identifier: MIT OR Apache-2.0
3

            
4
use reqwest::{Client, Url};
5
use serde::{Deserialize, Serialize};
6

            
7
use crate::ddi::client::Error;
8
use crate::ddi::deployment_base::{Chunk, Deployment};
9

            
10
#[derive(Debug)]
11
#[allow(dead_code)]
12
/// A pending confirmation whose details have not been retrieved yet.
13
///
14
/// Call [`ConfirmationRequest::fetch()`] to retrieve the details from server.
15
pub struct ConfirmationRequest {
16
    client: Client,
17
    url: String,
18
    details: Vec<String>,
19
}
20

            
21
impl ConfirmationRequest {
22
12
    pub(crate) fn new(client: Client, url: String) -> Self {
23
12
        Self {
24
12
            client,
25
12
            url,
26
12
            details: vec![],
27
12
        }
28
12
    }
29

            
30
    /// Confirm the confirmation request. The server should then proceed with the update and make a deploymentBase available.
31
5
    pub async fn confirm(self) -> Result<(), Error> {
32
2
        let confirmation = Confirmation::new(ConfirmationResponse::Confirmed, 1);
33

            
34
        // get feedback url
35
2
        let mut url: Url = self.url.parse()?;
36
        {
37
2
            let mut paths = url
38
2
                .path_segments_mut()
39
2
                .map_err(|_| url::ParseError::SetHostOnCannotBeABaseUrl)?;
40
2
            paths.push("feedback");
41
        }
42
2
        url.set_query(None);
43

            
44
2
        let reply = self
45
2
            .client
46
2
            .post(url.to_string())
47
2
            .json(&confirmation)
48
2
            .send()
49
2
            .await?;
50
2
        reply.error_for_status_ref()?;
51
2
        Ok(())
52
2
    }
53

            
54
    /// Decline the confirmation request. This will not change the status on the server and the same confirmation request will be received on the next poll.
55
5
    pub async fn decline(self) -> Result<(), Error> {
56
2
        let confirmation = Confirmation::new(ConfirmationResponse::Denied, -1);
57

            
58
        // get feedback url
59
2
        let mut url: Url = self.url.parse()?;
60
        {
61
2
            let mut paths = url
62
2
                .path_segments_mut()
63
2
                .map_err(|_| url::ParseError::SetHostOnCannotBeABaseUrl)?;
64
2
            paths.push("feedback");
65
        }
66
2
        url.set_query(None);
67

            
68
2
        let reply = self
69
2
            .client
70
2
            .post(url.to_string())
71
2
            .json(&confirmation)
72
2
            .send()
73
2
            .await?;
74
2
        reply.error_for_status_ref()?;
75
2
        Ok(())
76
2
    }
77

            
78
    /// Fetch the details of the update to be confirmed
79
15
    pub async fn update_info(&self) -> Result<ConfirmationInfo, Error> {
80
6
        let reply = self.client.get(&self.url).send().await?;
81
6
        reply.error_for_status_ref()?;
82

            
83
6
        let reply: Reply = reply.json().await?;
84
6
        Ok(ConfirmationInfo {
85
6
            reply,
86
6
            client: self.client.clone(),
87
6
        })
88
6
    }
89

            
90
    /// The metadata of all chunks of the update.
91
10
    pub async fn metadata(&self) -> Result<Vec<(String, String)>, Error> {
92
4
        let client = self.client.clone();
93

            
94
        // get update information from the server
95
4
        let update = self.update_info().await?.reply;
96

            
97
        // get all chunks of the update
98
4
        let chunks: Vec<Chunk> = update
99
4
            .confirmation
100
4
            .chunks
101
4
            .iter()
102
12
            .map(move |c| Chunk::new(c, client.clone()))
103
4
            .collect();
104

            
105
        // collect all metadata of each chunk
106
4
        let metadata = chunks
107
4
            .iter()
108
12
            .flat_map(|c| c.metadata().collect::<Vec<(&str, &str)>>())
109
12
            .map(|(k, v): (&str, &str)| (k.to_string(), v.to_string()))
110
4
            .collect();
111

            
112
4
        Ok(metadata)
113
4
    }
114
}
115

            
116
/// The downloaded details of a confirmation request.
117
#[derive(Debug)]
118
pub struct ConfirmationInfo {
119
    client: Client,
120
    reply: Reply,
121
}
122

            
123
impl ConfirmationInfo {
124
    /// Get all metadata of all chunks of the update.
125
4
    pub fn metadata(&self) -> Vec<(String, String)> {
126
        // get all chunks of the update
127
4
        let chunks: Vec<Chunk> = self
128
4
            .reply
129
4
            .confirmation
130
4
            .chunks
131
4
            .iter()
132
13
            .map(move |c| Chunk::new(c, self.client.clone()))
133
4
            .collect();
134

            
135
        // collect all metadata of each chunk
136
4
        let metadata = chunks
137
4
            .iter()
138
13
            .flat_map(|c| c.metadata().collect::<Vec<(&str, &str)>>())
139
13
            .map(|(k, v): (&str, &str)| (k.to_string(), v.to_string()))
140
4
            .collect();
141

            
142
4
        metadata
143
4
    }
144

            
145
    /// Get the action ID of the update to be confirmed.
146
4
    pub fn action_id(&self) -> &str {
147
4
        &self.reply.id
148
4
    }
149
}
150

            
151
#[derive(Debug, Deserialize)]
152
pub(crate) struct Reply {
153
    id: String,
154
    confirmation: Deployment,
155
}
156

            
157
/// The response to a confirmation request.
158
#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq)]
159
#[serde(rename_all = "lowercase")]
160
pub enum ConfirmationResponse {
161
    /// Proceed with the download
162
    Confirmed,
163
    /// Decline the confirmation and do not update
164
    Denied,
165
}
166

            
167
#[derive(Debug, Serialize)]
168
pub struct Confirmation {
169
    confirmation: ConfirmationResponse,
170
    code: i32,
171
    details: Vec<String>,
172
}
173

            
174
impl Confirmation {
175
8
    pub fn new(confirmation: ConfirmationResponse, code: i32) -> Self {
176
8
        Self {
177
8
            confirmation,
178
8
            code,
179
8
            details: vec![],
180
8
        }
181
8
    }
182
}