1use crate::codes::{Code, ErrorCode, Kind};
5
6#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("{message}")]
9pub struct Error {
10 pub code: ErrorCode,
11 pub message: String,
12 pub hint: Option<String>,
14}
15
16impl Error {
17 pub fn new(code: impl Into<ErrorCode>, message: impl Into<String>) -> Error {
18 Error {
19 code: code.into(),
20 message: message.into(),
21 hint: None,
22 }
23 }
24
25 pub fn with_hint(mut self, hint: impl Into<String>) -> Error {
26 self.hint = Some(hint.into());
27 self
28 }
29
30 pub fn kind(&self) -> Kind {
31 self.code.kind()
32 }
33
34 pub fn or(mut self, code: Code) -> Error {
37 if self.code == Code::RunFailed {
38 self.code = code.into();
39 }
40 self
41 }
42
43 pub fn from_plugin(e: &dre_protocol::host::HostError, message: impl Into<String>) -> Error {
46 let code = match e {
47 dre_protocol::host::HostError::Plugin {
48 code: Some(code),
49 kind,
50 ..
51 } => ErrorCode::Plugin {
52 code: code.clone(),
53 kind: kind.as_deref().and_then(Kind::parse).unwrap_or(Kind::Internal),
54 },
55 _ => ErrorCode::Core(Code::RunFailed),
56 };
57 Error {
58 code,
59 message: message.into(),
60 hint: None,
61 }
62 }
63}
64
65impl From<String> for Error {
67 fn from(message: String) -> Error {
68 Error::new(Code::RunFailed, message)
69 }
70}
71
72impl From<&str> for Error {
73 fn from(message: &str) -> Error {
74 Error::new(Code::RunFailed, message)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use dre_protocol::host::HostError;
82
83 fn plugin(kind: Option<&str>, code: Option<&str>) -> HostError {
84 HostError::Plugin {
85 plugin: "dre-plugin-sftp".into(),
86 message: "refused".into(),
87 kind: kind.map(str::to_string),
88 code: code.map(str::to_string),
89 }
90 }
91
92 #[test]
93 fn a_plugin_code_is_kept_with_its_kind() {
94 let e = Error::from_plugin(&plugin(Some("auth"), Some("sftp/bad-key")), "m").or(Code::DeliveryFailed);
95 assert_eq!(e.code.as_str(), "sftp/bad-key");
96 assert_eq!(e.kind(), Kind::Auth);
97 let e = Error::from_plugin(&plugin(None, Some("sftp/odd")), "m");
99 assert_eq!(e.kind(), Kind::Internal);
100 }
101
102 #[test]
103 fn without_a_code_the_step_gives_its_own() {
104 let e = Error::from_plugin(&plugin(Some("auth"), None), "m").or(Code::DeliveryFailed);
105 assert_eq!(e.code, Code::DeliveryFailed);
106 }
107}