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}