evergreen/
event.rs

1//! Evergreen API Response Events
2use crate as eg;
3use eg::date;
4use eg::EgValue;
5use std::fmt;
6
7/// Common argument to API calls that allow for targeted overrides.
8#[derive(Debug, PartialEq, Clone)]
9pub enum Overrides {
10    All,
11    Events(Vec<String>),
12}
13
14#[derive(Debug, Clone)]
15pub struct EgEvent {
16    code: isize,
17    textcode: String,
18    payload: EgValue, // EgValue::Null if empty
19    desc: Option<String>,
20    debug: Option<String>,
21    note: Option<String>,
22    servertime: Option<String>,
23    ilsperm: Option<String>,
24    ilspermloc: i64,
25    org: Option<i64>,
26    /// Some code adds ad-hoc bits to the event proper instead of putting
27    /// them into the "payload".
28    ad_hoc: Option<EgValue>,
29}
30
31impl fmt::Display for EgEvent {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        let mut s = format!("Event: {}:{}", self.code, self.textcode);
34
35        if let Some(ref d) = self.desc {
36            s = s + " -> " + d;
37        }
38
39        if let Some(ref p) = self.ilsperm {
40            s = format!("{} {}@{}", s, p, self.ilspermloc);
41        }
42
43        if let Some(ref n) = self.note {
44            s = s + "\n" + n;
45        }
46
47        write!(f, "{}", s)
48    }
49}
50
51impl From<EgEvent> for EgValue {
52    fn from(mut evt: EgEvent) -> Self {
53        let mut obj: EgValue = eg::hash! {
54            "code": evt.code(),
55            "textcode": evt.textcode(),
56            "payload": evt.payload_mut().take(),
57            "ilspermloc": evt.ilspermloc(),
58        };
59
60        if let Some(ad_hoc) = evt.ad_hoc.as_mut() {
61            for (k, v) in ad_hoc.entries_mut() {
62                obj[k] = v.take();
63            }
64        }
65
66        if let Some(v) = evt.desc() {
67            obj["desc"] = v.into();
68        }
69        if let Some(v) = evt.debug() {
70            obj["debug"] = v.into();
71        }
72        if let Some(v) = evt.note() {
73            obj["note"] = v.into();
74        }
75        if let Some(v) = evt.org() {
76            obj["org"] = v.into();
77        }
78        if let Some(v) = evt.servertime() {
79            obj["servertime"] = v.into();
80        }
81        if let Some(v) = evt.ilsperm() {
82            obj["ilsperm"] = v.into();
83        }
84
85        obj
86    }
87}
88
89impl From<&EgEvent> for EgValue {
90    fn from(evt: &EgEvent) -> Self {
91        EgValue::from(evt.clone())
92    }
93}
94
95impl EgEvent {
96    /// Create a new event with the provided code.
97    pub fn new(textcode: &str) -> Self {
98        let servertime = date::to_iso(&date::now());
99
100        EgEvent {
101            code: -1,
102            textcode: textcode.to_string(),
103            payload: EgValue::Null,
104            desc: None,
105            debug: None,
106            note: None,
107            org: None,
108            servertime: Some(servertime),
109            ilsperm: None,
110            ilspermloc: 0,
111            ad_hoc: None,
112        }
113    }
114
115    /// Shorthand for creating an event from a textcode as an EgValue.
116    pub fn value(textcode: &str) -> EgValue {
117        EgValue::from(EgEvent::new(textcode))
118    }
119
120    /// Shorthand for creating a SUCCESS event as an EgValue.
121    pub fn success_value() -> EgValue {
122        EgValue::from(EgEvent::success())
123    }
124
125    /// Create a new SUCCESS event
126    pub fn success() -> Self {
127        EgEvent::new("SUCCESS")
128    }
129
130    pub fn to_value(&self) -> EgValue {
131        self.into()
132    }
133
134    pub fn set_ils_perm(&mut self, p: &str) {
135        self.ilsperm = Some(p.to_string());
136    }
137
138    pub fn set_ils_perm_loc(&mut self, loc: i64) {
139        self.ilspermloc = loc;
140    }
141
142    pub fn code(&self) -> isize {
143        self.code
144    }
145
146    pub fn textcode(&self) -> &str {
147        &self.textcode
148    }
149
150    pub fn payload(&self) -> &EgValue {
151        &self.payload
152    }
153    pub fn payload_mut(&mut self) -> &mut EgValue {
154        &mut self.payload
155    }
156    pub fn set_payload(&mut self, payload: EgValue) {
157        self.payload = payload
158    }
159
160    /// Get the description of the EgEvent
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use evergreen::EgEvent;
166    ///
167    /// let mut event = EgEvent::new("INTERNAL_SERVER_ERROR");
168    /// assert!(event.desc().is_none());
169    ///
170    /// event.set_desc("Server Error: it did not go well :-(");
171    /// let new_description = event.desc();
172    /// if let Some(d) = new_description {
173    ///   println!("The event is described thusly: {}", d)
174    /// }
175    /// assert_eq!(new_description, Some("Server Error: it did not go well :-("));
176    /// ```
177    pub fn desc(&self) -> Option<&str> {
178        self.desc.as_deref()
179    }
180
181    pub fn debug(&self) -> Option<&str> {
182        self.debug.as_deref()
183    }
184
185    pub fn note(&self) -> Option<&str> {
186        self.note.as_deref()
187    }
188
189    pub fn servertime(&self) -> Option<&str> {
190        self.servertime.as_deref()
191    }
192
193    pub fn ilsperm(&self) -> Option<&str> {
194        self.ilsperm.as_deref()
195    }
196
197    pub fn ilspermloc(&self) -> i64 {
198        self.ilspermloc
199    }
200
201    pub fn is_success(&self) -> bool {
202        self.textcode.eq("SUCCESS")
203    }
204
205    pub fn org(&self) -> &Option<i64> {
206        &self.org
207    }
208    pub fn set_org(&mut self, id: i64) {
209        self.org = Some(id);
210    }
211
212    pub fn ad_hoc(&self) -> Option<&EgValue> {
213        self.ad_hoc.as_ref()
214    }
215
216    pub fn set_desc(&mut self, s: &str) {
217        self.desc = Some(s.to_string());
218    }
219
220    pub fn set_debug(&mut self, s: &str) {
221        self.debug = Some(s.to_string());
222    }
223
224    pub fn set_note(&mut self, s: &str) {
225        self.note = Some(s.to_string());
226    }
227
228    pub fn set_ad_hoc_value(&mut self, key: &str, value: EgValue) {
229        if self.ad_hoc.is_none() {
230            self.ad_hoc = Some(EgValue::new_object());
231        }
232
233        let ad_hoc = self.ad_hoc.as_mut().unwrap();
234        ad_hoc[key] = value;
235    }
236
237    /// Parses a EgValue and optionally returns an EgEvent.
238    ///
239    /// ```
240    /// use evergreen as eg;
241    /// use eg::EgEvent;
242    /// use eg::EgValue;
243    ///
244    /// let jv = eg::hash! {
245    ///     code: EgValue::from(100),
246    ///     textcode: EgValue::from("SUCCESS"),
247    ///     ilsperm: EgValue::from("STAFF_LOGIN"),
248    ///     ilspermloc: 1,
249    ///     foo: EgValue::from("bar"),
250    /// };
251    ///
252    /// let evt = EgEvent::parse(&jv).expect("Event Parsing Failed");
253    /// assert!(evt.is_success());
254    ///
255    /// assert_eq!(format!("{}", evt), String::from("Event: -1:SUCCESS STAFF_LOGIN@1"));
256    /// assert!(evt.ad_hoc().unwrap().has_key("foo"));
257    ///
258    /// let jv2 = eg::hash! {
259    ///     howdy: EgValue::from(123)
260    /// };
261    ///
262    /// let evt_op = EgEvent::parse(&jv2);
263    /// assert!(evt_op.is_none());
264    /// ```
265    pub fn parse(jv: &EgValue) -> Option<EgEvent> {
266        if !jv.is_object() || jv.is_blessed() {
267            return None;
268        }
269
270        // textcode is the only required field.
271        let textcode = match jv["textcode"].as_str() {
272            Some(c) => String::from(c),
273            _ => return None,
274        };
275
276        let mut evt = EgEvent::new(&textcode);
277        evt.set_payload(jv["payload"].clone());
278
279        if let Some(code) = jv["ilsevent"].as_isize() {
280            evt.code = code;
281        };
282
283        if let Some(permloc) = jv["ilspermloc"].as_i64() {
284            evt.ilspermloc = permloc;
285        }
286
287        if let Some(org) = jv["org"].as_i64() {
288            evt.org = Some(org);
289        }
290
291        let mut ad_hoc = EgValue::new_object();
292        for (field, value) in jv.entries() {
293            match field {
294                "textcode" | "payload" | "ilsevent" | "ilspermloc" | "org" => {
295                    // These are already handled above.
296                }
297                "desc" | "debug" | "note" | "servertime" | "ilsperm" => {
298                    // These are well-known string values.
299                    if let Some(v) = value.as_str() {
300                        match field {
301                            "desc" => evt.desc = Some(v.to_string()),
302                            "debug" => evt.debug = Some(v.to_string()),
303                            "note" => evt.note = Some(v.to_string()),
304                            "servertime" => evt.servertime = Some(v.to_string()),
305                            "ilsperm" => evt.ilsperm = Some(v.to_string()),
306                            _ => {} // shold not happen
307                        }
308                    }
309                }
310                // Tack any unknown values onto the ad_hoc blob.
311                _ => ad_hoc[field] = value.clone(),
312            }
313        }
314
315        if !ad_hoc.is_empty() {
316            evt.ad_hoc = Some(ad_hoc);
317        }
318
319        Some(evt)
320    }
321}