1use std::fmt::Display;
2
3use log::Level;
4use nethsm_sdk_rs::models::{Switch, UnattendedBootConfig};
5use serde::{Deserialize, Serialize};
6use ureq::Response;
7
8#[derive(Debug, Deserialize)]
13pub struct Message {
14 message: String,
15}
16
17impl From<Response> for Message {
18 fn from(value: Response) -> Self {
19 if let Ok(message) = value.into_json() {
20 message
21 } else {
22 Message {
23 message: "Deserialization error (no message in body)".to_string(),
24 }
25 }
26 }
27}
28
29impl Display for Message {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.write_str(&self.message)
32 }
33}
34
35#[derive(Debug)]
36pub struct ApiErrorMessage {
37 pub status_code: u16,
38 pub message: Message,
39}
40
41impl From<(u16, Message)> for ApiErrorMessage {
42 fn from(value: (u16, Message)) -> Self {
43 Self {
44 status_code: value.0,
45 message: value.1,
46 }
47 }
48}
49
50impl Display for ApiErrorMessage {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.write_str(&format!(
53 "{} (status code {})",
54 self.message, self.status_code
55 ))
56 }
57}
58
59pub struct NetHsmApiError<T> {
64 error: Option<nethsm_sdk_rs::apis::Error<T>>,
65 message: Option<String>,
66}
67
68impl<T> From<nethsm_sdk_rs::apis::Error<T>> for NetHsmApiError<T> {
69 fn from(value: nethsm_sdk_rs::apis::Error<T>) -> Self {
70 match value {
71 nethsm_sdk_rs::apis::Error::Ureq(error) => match error {
72 nethsm_sdk_rs::ureq::Error::Status(code, response) => Self {
73 error: None,
74 message: Some(ApiErrorMessage::from((code, response.into())).to_string()),
75 },
76 nethsm_sdk_rs::ureq::Error::Transport(transport) => Self {
77 error: None,
78 message: Some(format!("{transport}")),
79 },
80 },
81 nethsm_sdk_rs::apis::Error::ResponseError(resp) => Self {
82 error: None,
83 message: Some(format!(
84 "Status code: {}: {}",
85 resp.status,
86 serde_json::from_slice::<Message>(&resp.content)
89 .map(|m| m.message)
90 .unwrap_or_else(|_| String::from_utf8_lossy(&resp.content).into())
92 )),
93 },
94 _ => Self {
95 error: Some(value),
96 message: None,
97 },
98 }
99 }
100}
101
102impl<T> Display for NetHsmApiError<T> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 if let Some(message) = self.message.as_ref() {
105 write!(f, "{message}")?;
106 } else if let Some(error) = self.error.as_ref() {
107 write!(f, "{error}")?;
108 }
109 Ok(())
110 }
111}
112
113#[derive(
119 Clone,
120 Copy,
121 Debug,
122 strum::Display,
123 strum::EnumString,
124 strum::EnumIter,
125 strum::IntoStaticStr,
126 Eq,
127 PartialEq,
128)]
129#[strum(ascii_case_insensitive)]
130pub enum BootMode {
131 Attended,
134 Unattended,
137}
138
139impl From<UnattendedBootConfig> for BootMode {
140 fn from(value: UnattendedBootConfig) -> Self {
141 match value.status {
142 Switch::On => BootMode::Unattended,
143 Switch::Off => BootMode::Attended,
144 }
145 }
146}
147
148impl From<BootMode> for UnattendedBootConfig {
149 fn from(value: BootMode) -> Self {
150 match value {
151 BootMode::Unattended => UnattendedBootConfig { status: Switch::On },
152 BootMode::Attended => UnattendedBootConfig {
153 status: Switch::Off,
154 },
155 }
156 }
157}
158
159#[derive(
161 Clone,
162 Copy,
163 Debug,
164 Default,
165 Deserialize,
166 strum::Display,
167 strum::EnumString,
168 strum::EnumIter,
169 strum::IntoStaticStr,
170 Eq,
171 Hash,
172 Ord,
173 PartialEq,
174 PartialOrd,
175 Serialize,
176)]
177#[strum(ascii_case_insensitive)]
178pub enum LogLevel {
179 Debug,
181
182 Error,
184
185 #[default]
187 Info,
188
189 Warning,
191}
192
193impl From<LogLevel> for nethsm_sdk_rs::models::LogLevel {
194 fn from(value: LogLevel) -> Self {
195 match value {
196 LogLevel::Debug => Self::Debug,
197 LogLevel::Error => Self::Error,
198 LogLevel::Info => Self::Info,
199 LogLevel::Warning => Self::Warning,
200 }
201 }
202}
203
204impl From<Level> for LogLevel {
205 fn from(value: Level) -> Self {
211 match value {
212 Level::Trace => Self::Debug,
213 Level::Debug => Self::Debug,
214 Level::Error => Self::Error,
215 Level::Info => Self::Info,
216 Level::Warn => Self::Warning,
217 }
218 }
219}
220
221#[derive(
223 Clone,
224 Copy,
225 Debug,
226 Default,
227 Deserialize,
228 strum::Display,
229 strum::EnumString,
230 strum::EnumIter,
231 strum::IntoStaticStr,
232 Eq,
233 Hash,
234 Ord,
235 PartialEq,
236 PartialOrd,
237 Serialize,
238)]
239#[strum(ascii_case_insensitive)]
240pub enum TlsKeyType {
241 Curve25519,
243
244 EcP224,
246
247 EcP256,
249
250 EcP384,
252
253 EcP521,
255
256 #[default]
258 Rsa,
259}
260
261impl From<TlsKeyType> for nethsm_sdk_rs::models::TlsKeyType {
262 fn from(value: TlsKeyType) -> Self {
263 match value {
264 TlsKeyType::Curve25519 => Self::Curve25519,
265 TlsKeyType::EcP224 => Self::EcP224,
266 TlsKeyType::EcP256 => Self::EcP256,
267 TlsKeyType::EcP384 => Self::EcP384,
268 TlsKeyType::EcP521 => Self::EcP521,
269 TlsKeyType::Rsa => Self::Rsa,
270 }
271 }
272}
273
274#[derive(
276 Clone,
277 Copy,
278 Debug,
279 Default,
280 Deserialize,
281 strum::Display,
282 strum::EnumString,
283 strum::EnumIter,
284 strum::IntoStaticStr,
285 Eq,
286 PartialEq,
287 Ord,
288 PartialOrd,
289 Hash,
290 Serialize,
291)]
292#[strum(ascii_case_insensitive)]
293pub enum UserRole {
294 Administrator,
296 Backup,
298 Metrics,
300 #[default]
302 Operator,
303}
304
305impl From<UserRole> for nethsm_sdk_rs::models::UserRole {
306 fn from(value: UserRole) -> Self {
307 match value {
308 UserRole::Administrator => Self::Administrator,
309 UserRole::Backup => Self::Backup,
310 UserRole::Metrics => Self::Metrics,
311 UserRole::Operator => Self::Operator,
312 }
313 }
314}
315
316impl From<nethsm_sdk_rs::models::UserRole> for UserRole {
317 fn from(value: nethsm_sdk_rs::models::UserRole) -> Self {
318 match value {
319 nethsm_sdk_rs::models::UserRole::Administrator => Self::Administrator,
320 nethsm_sdk_rs::models::UserRole::Backup => Self::Backup,
321 nethsm_sdk_rs::models::UserRole::Metrics => Self::Metrics,
322 nethsm_sdk_rs::models::UserRole::Operator => Self::Operator,
323 }
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use std::str::FromStr;
330
331 use rstest::rstest;
332 use testresult::TestResult;
333
334 use super::*;
335
336 #[rstest]
337 #[case("rsa", Some(TlsKeyType::Rsa))]
338 #[case("curve25519", Some(TlsKeyType::Curve25519))]
339 #[case("ecp256", Some(TlsKeyType::EcP256))]
340 #[case("ecp384", Some(TlsKeyType::EcP384))]
341 #[case("ecp521", Some(TlsKeyType::EcP521))]
342 #[case("foo", None)]
343 fn tlskeytype_fromstr(#[case] input: &str, #[case] expected: Option<TlsKeyType>) -> TestResult {
344 if let Some(expected) = expected {
345 assert_eq!(TlsKeyType::from_str(input)?, expected);
346 } else {
347 assert!(TlsKeyType::from_str(input).is_err());
348 }
349 Ok(())
350 }
351
352 #[rstest]
353 #[case("administrator", Some(UserRole::Administrator))]
354 #[case("backup", Some(UserRole::Backup))]
355 #[case("metrics", Some(UserRole::Metrics))]
356 #[case("operator", Some(UserRole::Operator))]
357 #[case("foo", None)]
358 fn userrole_fromstr(#[case] input: &str, #[case] expected: Option<UserRole>) -> TestResult {
359 if let Some(expected) = expected {
360 assert_eq!(UserRole::from_str(input)?, expected);
361 } else {
362 assert!(UserRole::from_str(input).is_err());
363 }
364 Ok(())
365 }
366}