evergreen/
result.rs

1//! Common result type for methods/fuctions which may return a `Result`.
2
3use crate::event::EgEvent;
4use std::fmt;
5
6/// This is a convenient way to set the error type to EgError on common
7/// method/function responses to simplify the declaration of return types.
8/// ```
9/// use evergreen::result::*;
10/// use evergreen::event::*;
11///
12/// let res = EgResult::Ok("Hello");
13/// assert_eq!(res.unwrap(), "Hello");
14///
15/// fn foo1() -> EgResult<()> {
16///   let evt = EgEvent::new("PROBLEM");
17///   let err = EgError::from_event(evt);
18///   Err(err)
19/// }
20///
21/// // Same result as above.
22/// fn foo2() -> EgResult<()> {
23///   Err(EgEvent::new("PROBLEM").into())
24/// }
25///
26/// // Same result as above
27/// fn foo3() -> EgResult<()> {
28///   Err(EgEvent::new("PROBLEM"))?;
29///   Ok(())
30/// }
31///
32/// if let EgError::Event(e) = foo1().err().unwrap() {
33///     assert_eq!(e.textcode(), "PROBLEM");
34/// } else {
35///     panic!("unexpected response");
36/// }
37///
38/// if let EgError::Event(e) = foo2().err().unwrap() {
39///     assert_eq!(e.textcode(), "PROBLEM");
40/// } else {
41///     panic!("unexpected response");
42/// }
43///
44/// if let EgError::Event(e) = foo3().err().unwrap() {
45///     assert_eq!(e.textcode(), "PROBLEM");
46/// } else {
47///     panic!("unexpected response");
48/// }
49///
50/// ```
51pub type EgResult<T> = std::result::Result<T, EgError>;
52
53#[derive(Debug, Clone)]
54pub enum EgError {
55    /// General error/failure messages that is not linked to an EgEvent.
56    ///
57    /// For one thing, this is useful for encapsulating OpenSRF's generic
58    /// fatal error strings.
59    Debug(Box<String>),
60    Event(Box<EgEvent>),
61}
62
63impl std::error::Error for EgError {
64    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
65        None
66    }
67}
68
69impl EgError {
70    /// Coerce the EgError into an EgEvent regardless of its internal
71    /// type.
72    ///
73    /// If the error is a Debug(string) type, return a new
74    /// INTERNAL_SERVER_ERROR event containing the error string.
75    /// Otherwise, return a copy of the contained event.
76    pub fn event_or_default(&self) -> EgEvent {
77        match self {
78            EgError::Event(e) => *e.clone(),
79            EgError::Debug(s) => {
80                let mut evt = EgEvent::new("INTERNAL_SERVER_ERROR");
81                // This is for debug purposes only -- i18n not needed.
82                evt.set_desc(&format!("Server Error: {s}"));
83                evt
84            }
85        }
86    }
87
88    pub fn from_event(e: EgEvent) -> EgError {
89        Self::Event(Box::new(e))
90    }
91
92    pub fn from_string(s: String) -> EgError {
93        Self::Debug(Box::new(s))
94    }
95}
96
97impl fmt::Display for EgError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        match *self {
100            Self::Debug(ref m) => write!(f, "{m}"),
101            Self::Event(ref e) => write!(f, "{e}"),
102        }
103    }
104}
105
106/// Useful for translating generic OSRF Err(String)'s into EgError's
107impl From<String> for EgError {
108    fn from(msg: String) -> Self {
109        EgError::from_string(msg)
110    }
111}
112
113impl From<&str> for EgError {
114    fn from(msg: &str) -> Self {
115        EgError::from_string(msg.to_string())
116    }
117}
118
119/// Useful for translating EgError's into plain strings for
120/// methods/functions that return vanilla Result<T, String>, like
121/// OpenSRF published APIs
122impl From<EgError> for String {
123    fn from(err: EgError) -> Self {
124        match err {
125            EgError::Debug(m) => m.to_string(),
126            EgError::Event(e) => e.to_string(),
127        }
128    }
129}
130
131/// Useful for translating EgEvents that are returned as Err's into
132/// fully-fledged Err(EgError) responses.
133impl From<EgEvent> for EgError {
134    fn from(evt: EgEvent) -> Self {
135        EgError::from_event(evt)
136    }
137}
138
139/// Postgres errors can also be converted into EgErrors.  This allows you
140/// to use the question mark operator `?` to propagate postgres errors up
141/// the stack as EgErrors.
142/// # Example
143/// ```
144/// fn connect_to_a_nonexistant_db() -> evergreen::EgResult<()> {
145///   postgres::Client::connect("bad-bad-connection-string", postgres::NoTls)?;
146///   Ok(())
147/// }
148///
149/// let result = connect_to_a_nonexistant_db();
150/// assert!(result.err().unwrap().to_string().contains("invalid connection string"));
151/// ```
152impl From<postgres::Error> for EgError {
153    fn from(original: postgres::Error) -> Self {
154        EgError::from_string(original.to_string())
155    }
156}
157
158/// ```
159/// use evergreen::event::*;
160/// use evergreen::result::*;
161///
162/// fn foo() -> Result<(), EgError> {
163///     let evt = EgEvent::new("PROBLEM");
164///     Err(evt.into())
165/// }
166///
167/// if let Err(e) = foo() {
168///     if let EgError::Event(ee) = e {
169///         assert_eq!(ee.textcode(), "PROBLEM");
170///     } else {
171///         panic!("Unexpected EgError type: {}", e);
172///     }
173/// } else {
174///     panic!("Unexpected result type");
175/// }
176/// ```
177impl From<&EgEvent> for EgError {
178    fn from(evt: &EgEvent) -> Self {
179        EgError::from_event(evt.clone())
180    }
181}