1use std::fmt::Display;
2
3use log::Level;
4use nethsm_sdk_rs::models::{
5 KeyType,
6 Switch,
7 UnattendedBootConfig,
8 UserRole as NetHsmSdkRsUserRole,
9};
10use nethsm_sdk_rs::ureq::http::response::Response;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, thiserror::Error)]
14pub enum Error {
15 #[error("The nethsm-sdk-rs key type {key_type} is not supported by Signstar")]
17 NetHsmSdkRsKeyTypeUnsupportedInSignstar {
18 key_type: KeyType,
20 },
21
22 #[error("The nethsm-sdk-rs switch {switch} is not supported by Signstar")]
24 NetHsmSdkRsSwitchUnsupportedInSignstar {
25 switch: Switch,
27 },
28
29 #[error("The nethsm-sdk-rs user role {user_role} is not supported by Signstar")]
31 NetHsmSdkRsUserRoleUnsupportedInSignstar {
32 user_role: NetHsmSdkRsUserRole,
34 },
35
36 #[error("The TLS key type {tls_key_type} is not supported by nethsm-sdk-rs")]
38 TlsKeyTypeUnsupportedInNetHsmSdkRs {
39 tls_key_type: TlsKeyType,
41 },
42}
43
44#[derive(Debug, Deserialize)]
49pub struct Message {
50 message: String,
51}
52
53impl From<Response<String>> for Message {
54 fn from(value: Response<String>) -> Self {
55 Message {
56 message: value.into_body(),
57 }
58 }
59}
60
61impl Display for Message {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.write_str(&self.message)
64 }
65}
66
67#[derive(Debug)]
68pub struct ApiErrorMessage {
69 pub status_code: u16,
70 pub message: Message,
71}
72
73impl From<(u16, Message)> for ApiErrorMessage {
74 fn from(value: (u16, Message)) -> Self {
75 Self {
76 status_code: value.0,
77 message: value.1,
78 }
79 }
80}
81
82impl Display for ApiErrorMessage {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.write_str(&format!(
85 "{} (status code {})",
86 self.message, self.status_code
87 ))
88 }
89}
90
91pub struct NetHsmApiError<T> {
96 error: Option<nethsm_sdk_rs::apis::Error<T>>,
97 message: Option<String>,
98}
99
100impl<T> From<nethsm_sdk_rs::apis::Error<T>> for NetHsmApiError<T> {
101 fn from(value: nethsm_sdk_rs::apis::Error<T>) -> Self {
102 match &value {
103 nethsm_sdk_rs::apis::Error::Ureq(error) => match error {
104 nethsm_sdk_rs::ureq::Error::StatusCode(code) => Self {
105 error: None,
106 message: Some(
107 ApiErrorMessage::from((
108 *code,
109 Message {
110 message: "".to_string(),
111 },
112 ))
113 .to_string(),
114 ),
115 },
116 nethsm_sdk_rs::ureq::Error::Http(transport) => Self {
117 error: None,
118 message: Some(format!("{transport}")),
119 },
120 _ => Self {
121 error: Some(value),
122 message: None,
123 },
124 },
125 nethsm_sdk_rs::apis::Error::ResponseError(resp) => Self {
126 error: None,
127 message: Some(format!(
128 "Status code: {}: {}",
129 resp.status,
130 serde_json::from_slice::<Message>(&resp.content)
133 .map(|m| m.message)
134 .unwrap_or_else(|_| String::from_utf8_lossy(&resp.content).into())
136 )),
137 },
138 _ => Self {
139 error: Some(value),
140 message: None,
141 },
142 }
143 }
144}
145
146impl<T> Display for NetHsmApiError<T> {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 if let Some(message) = self.message.as_ref() {
149 write!(f, "{message}")?;
150 } else if let Some(error) = self.error.as_ref() {
151 write!(f, "{error}")?;
152 }
153 Ok(())
154 }
155}
156
157#[derive(
163 Clone,
164 Copy,
165 Debug,
166 strum::Display,
167 strum::EnumString,
168 strum::EnumIter,
169 strum::IntoStaticStr,
170 Eq,
171 PartialEq,
172)]
173#[strum(ascii_case_insensitive)]
174pub enum BootMode {
175 Attended,
178 Unattended,
181}
182
183impl TryFrom<UnattendedBootConfig> for BootMode {
184 type Error = crate::Error;
185
186 fn try_from(value: UnattendedBootConfig) -> Result<Self, Self::Error> {
187 Ok(match value.status {
188 Switch::On => BootMode::Unattended,
189 Switch::Off => BootMode::Attended,
190 switch => return Err(Error::NetHsmSdkRsSwitchUnsupportedInSignstar { switch }.into()),
195 })
196 }
197}
198
199impl From<BootMode> for UnattendedBootConfig {
200 fn from(value: BootMode) -> Self {
201 match value {
202 BootMode::Unattended => UnattendedBootConfig::new(Switch::On),
203 BootMode::Attended => UnattendedBootConfig::new(Switch::Off),
204 }
205 }
206}
207
208#[derive(
210 Clone,
211 Copy,
212 Debug,
213 Default,
214 Deserialize,
215 strum::Display,
216 strum::EnumString,
217 strum::EnumIter,
218 strum::IntoStaticStr,
219 Eq,
220 Hash,
221 Ord,
222 PartialEq,
223 PartialOrd,
224 Serialize,
225)]
226#[strum(ascii_case_insensitive)]
227pub enum LogLevel {
228 Debug,
230
231 Error,
233
234 #[default]
236 Info,
237
238 Warning,
240}
241
242impl From<LogLevel> for nethsm_sdk_rs::models::LogLevel {
243 fn from(value: LogLevel) -> Self {
244 match value {
245 LogLevel::Debug => Self::Debug,
246 LogLevel::Error => Self::Error,
247 LogLevel::Info => Self::Info,
248 LogLevel::Warning => Self::Warning,
249 }
250 }
251}
252
253impl From<Level> for LogLevel {
254 fn from(value: Level) -> Self {
260 match value {
261 Level::Trace => Self::Debug,
262 Level::Debug => Self::Debug,
263 Level::Error => Self::Error,
264 Level::Info => Self::Info,
265 Level::Warn => Self::Warning,
266 }
267 }
268}
269
270#[derive(
272 Clone,
273 Copy,
274 Debug,
275 Default,
276 Deserialize,
277 strum::Display,
278 strum::EnumString,
279 strum::EnumIter,
280 strum::IntoStaticStr,
281 Eq,
282 Hash,
283 Ord,
284 PartialEq,
285 PartialOrd,
286 Serialize,
287)]
288#[strum(ascii_case_insensitive)]
289pub enum TlsKeyType {
290 Curve25519,
292
293 EcBp256,
295
296 EcBp384,
298
299 EcBp512,
301
302 EcP224,
304
305 EcP256,
307
308 EcP384,
310
311 EcP521,
313
314 #[default]
316 Rsa,
317}
318
319impl TryFrom<TlsKeyType> for nethsm_sdk_rs::models::TlsKeyType {
320 type Error = crate::Error;
321
322 fn try_from(value: TlsKeyType) -> Result<Self, Self::Error> {
323 Ok(match value {
324 TlsKeyType::Curve25519 => Self::Curve25519,
325 TlsKeyType::EcBp256 => Self::BrainpoolP256,
326 TlsKeyType::EcBp384 => Self::BrainpoolP384,
327 TlsKeyType::EcBp512 => Self::BrainpoolP512,
328 TlsKeyType::EcP224 => {
329 return Err(Error::TlsKeyTypeUnsupportedInNetHsmSdkRs {
330 tls_key_type: value,
331 }
332 .into());
333 }
334 TlsKeyType::EcP256 => Self::EcP256,
335 TlsKeyType::EcP384 => Self::EcP384,
336 TlsKeyType::EcP521 => Self::EcP521,
337 TlsKeyType::Rsa => Self::Rsa,
338 })
339 }
340}
341
342#[derive(
344 Clone,
345 Copy,
346 Debug,
347 Default,
348 Deserialize,
349 strum::Display,
350 strum::EnumString,
351 strum::EnumIter,
352 strum::IntoStaticStr,
353 Eq,
354 PartialEq,
355 Ord,
356 PartialOrd,
357 Hash,
358 Serialize,
359)]
360#[strum(ascii_case_insensitive)]
361pub enum UserRole {
362 Administrator,
364 Backup,
366 Metrics,
368 #[default]
370 Operator,
371}
372
373impl TryFrom<UserRole> for NetHsmSdkRsUserRole {
374 type Error = crate::Error;
375
376 fn try_from(value: UserRole) -> Result<Self, Self::Error> {
377 Ok(match value {
378 UserRole::Administrator => Self::Administrator,
379 UserRole::Backup => Self::Backup,
380 UserRole::Metrics => Self::Metrics,
381 UserRole::Operator => Self::Operator,
382 })
383 }
384}
385
386impl TryFrom<NetHsmSdkRsUserRole> for UserRole {
387 type Error = crate::Error;
388
389 fn try_from(value: NetHsmSdkRsUserRole) -> Result<Self, Self::Error> {
390 Ok(match value {
391 NetHsmSdkRsUserRole::Administrator => Self::Administrator,
392 NetHsmSdkRsUserRole::Backup => Self::Backup,
393 NetHsmSdkRsUserRole::Metrics => Self::Metrics,
394 NetHsmSdkRsUserRole::Operator => Self::Operator,
395 user_role => {
400 return Err(Error::NetHsmSdkRsUserRoleUnsupportedInSignstar { user_role }.into());
401 }
402 })
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use std::str::FromStr;
409
410 use rstest::rstest;
411 use testresult::TestResult;
412
413 use super::*;
414
415 #[rstest]
416 #[case("rsa", Some(TlsKeyType::Rsa))]
417 #[case("curve25519", Some(TlsKeyType::Curve25519))]
418 #[case("ecp256", Some(TlsKeyType::EcP256))]
419 #[case("ecp384", Some(TlsKeyType::EcP384))]
420 #[case("ecp521", Some(TlsKeyType::EcP521))]
421 #[case("foo", None)]
422 fn tlskeytype_fromstr(#[case] input: &str, #[case] expected: Option<TlsKeyType>) -> TestResult {
423 if let Some(expected) = expected {
424 assert_eq!(TlsKeyType::from_str(input)?, expected);
425 } else {
426 assert!(TlsKeyType::from_str(input).is_err());
427 }
428 Ok(())
429 }
430
431 #[rstest]
432 #[case("administrator", Some(UserRole::Administrator))]
433 #[case("backup", Some(UserRole::Backup))]
434 #[case("metrics", Some(UserRole::Metrics))]
435 #[case("operator", Some(UserRole::Operator))]
436 #[case("foo", None)]
437 fn userrole_fromstr(#[case] input: &str, #[case] expected: Option<UserRole>) -> TestResult {
438 if let Some(expected) = expected {
439 assert_eq!(UserRole::from_str(input)?, expected);
440 } else {
441 assert!(UserRole::from_str(input).is_err());
442 }
443 Ok(())
444 }
445}