1use std::collections::HashMap;
2use std::error::Error;
9use std::fmt::{Display, Formatter, Result as FmtResult};
10use time::OffsetDateTime;
11
12#[derive(Debug, PartialEq)]
20pub enum CefConversionError {
21 Unexpected(String),
22}
23impl Error for CefConversionError {}
24impl Display for CefConversionError {
25 fn fmt(&self, f: &mut Formatter) -> FmtResult {
26 match self {
27 CefConversionError::Unexpected(message) => {
28 write!(f, "CefConversionError::Unexpected {}", message)
29 }
30 }
31 }
32}
33
34pub type CefResult = Result<String, CefConversionError>;
37
38pub type CefExtensionsResult = Result<(), CefConversionError>;
42
43pub trait CefHeaderVersion {
45 fn cef_header_version(&self) -> CefResult;
46}
47
48pub trait CefHeaderDeviceVendor {
50 fn cef_header_device_vendor(&self) -> CefResult;
51}
52
53pub trait CefHeaderDeviceProduct {
55 fn cef_header_device_product(&self) -> CefResult;
56}
57
58pub trait CefHeaderDeviceVersion {
60 fn cef_header_device_version(&self) -> CefResult;
61}
62
63pub trait CefHeaderDeviceEventClassID {
65 fn cef_header_device_event_class_id(&self) -> CefResult;
66}
67
68pub trait CefHeaderName {
70 fn cef_header_name(&self) -> CefResult;
71}
72
73pub trait CefHeaderSeverity {
75 fn cef_header_severity(&self) -> CefResult;
76}
77
78pub trait CefExtensions {
83 fn cef_extensions(&self, collector: &mut HashMap<String, String>) -> CefExtensionsResult;
84}
85
86pub trait ToCef:
90 CefHeaderVersion
91 + CefHeaderDeviceVendor
92 + CefHeaderDeviceProduct
93 + CefHeaderDeviceVersion
94 + CefHeaderDeviceEventClassID
95 + CefHeaderName
96 + CefHeaderSeverity
97 + CefExtensions
98{
99 fn to_cef(&self) -> CefResult {
100 let mut extensions: HashMap<String, String> = HashMap::new();
101
102 if let Err(err) = self.cef_extensions(&mut extensions) {
104 return Err(err);
105 };
106
107 let mut kvstrs: Vec<String> = extensions
109 .into_iter()
110 .map(|(key, value)| [key, value].join("="))
111 .collect();
112
113 kvstrs.sort_unstable();
114
115 let extensionsstr = kvstrs.join(" ");
117
118 let mut cef_entry = String::new();
119 cef_entry.push_str("CEF:");
120 cef_entry.push_str(&self.cef_header_version()?);
121 cef_entry.push('|');
122 cef_entry.push_str(&self.cef_header_device_vendor()?);
123 cef_entry.push('|');
124 cef_entry.push_str(&self.cef_header_device_product()?);
125 cef_entry.push('|');
126 cef_entry.push_str(&self.cef_header_device_version()?);
127 cef_entry.push('|');
128 cef_entry.push_str(&self.cef_header_device_event_class_id()?);
129 cef_entry.push('|');
130 cef_entry.push_str(&self.cef_header_name()?);
131 cef_entry.push('|');
132 cef_entry.push_str(&self.cef_header_severity()?);
133 cef_entry.push('|');
134 cef_entry.push_str(extensionsstr.as_str());
135
136 Ok(cef_entry)
137 }
138}
139
140impl CefExtensions for OffsetDateTime {
143 fn cef_extensions(&self, collector: &mut HashMap<String, String>) -> CefExtensionsResult {
147 collector.insert(
148 "rt".to_owned(),
149 format!("{}", self.unix_timestamp_nanos() / 1000000),
150 );
151 Ok(())
152 }
153}
154
155#[cfg(test)]
159mod test {
160 use super::*;
161
162 struct GoodExample {}
163
164 impl ToCef for GoodExample {}
165 impl CefHeaderVersion for GoodExample {
166 fn cef_header_version(&self) -> CefResult {
167 Ok("0".to_owned())
168 }
169 }
170
171 impl CefHeaderDeviceVendor for GoodExample {
172 fn cef_header_device_vendor(&self) -> CefResult {
173 Ok("polyverse".to_owned())
174 }
175 }
176
177 impl CefHeaderDeviceProduct for GoodExample {
178 fn cef_header_device_product(&self) -> CefResult {
179 Ok("zerotect".to_owned())
180 }
181 }
182
183 impl CefHeaderDeviceVersion for GoodExample {
184 fn cef_header_device_version(&self) -> CefResult {
185 Ok("V1".to_owned())
186 }
187 }
188
189 impl CefHeaderDeviceEventClassID for GoodExample {
190 fn cef_header_device_event_class_id(&self) -> CefResult {
191 Ok("LinuxKernelTrap".to_owned())
192 }
193 }
194
195 impl CefHeaderName for GoodExample {
196 fn cef_header_name(&self) -> CefResult {
197 Ok("Linux Kernel Trap".to_owned())
198 }
199 }
200
201 impl CefHeaderSeverity for GoodExample {
202 fn cef_header_severity(&self) -> CefResult {
203 Ok("10".to_owned())
204 }
205 }
206
207 impl CefExtensions for GoodExample {
208 fn cef_extensions(&self, collector: &mut HashMap<String, String>) -> CefExtensionsResult {
209 collector.insert("customField1".to_owned(), "customValue1".to_owned());
210 collector.insert("customField2".to_owned(), "customValue2".to_owned());
211 collector.insert("customField3".to_owned(), "customValue2".to_owned());
212 collector.insert("customField4".to_owned(), "customValue3".to_owned());
213 Ok(())
214 }
215 }
216
217 struct BadExample {}
218 impl ToCef for BadExample {}
219 impl CefHeaderVersion for BadExample {
220 fn cef_header_version(&self) -> CefResult {
221 Ok("0".to_owned())
222 }
223 }
224
225 impl CefHeaderDeviceVendor for BadExample {
226 fn cef_header_device_vendor(&self) -> CefResult {
227 Ok("polyverse".to_owned())
228 }
229 }
230
231 impl CefHeaderDeviceProduct for BadExample {
232 fn cef_header_device_product(&self) -> CefResult {
233 Ok("zerotect".to_owned())
234 }
235 }
236
237 impl CefHeaderDeviceVersion for BadExample {
238 fn cef_header_device_version(&self) -> CefResult {
239 Ok("V1".to_owned())
240 }
241 }
242
243 impl CefHeaderDeviceEventClassID for BadExample {
244 fn cef_header_device_event_class_id(&self) -> CefResult {
245 Err(CefConversionError::Unexpected(
246 "This error should propagate".to_owned(),
247 ))
248 }
249 }
250
251 impl CefHeaderName for BadExample {
252 fn cef_header_name(&self) -> CefResult {
253 Ok("Linux Kernel Trap".to_owned())
254 }
255 }
256
257 impl CefHeaderSeverity for BadExample {
258 fn cef_header_severity(&self) -> CefResult {
259 Ok("10".to_owned())
260 }
261 }
262
263 impl CefExtensions for BadExample {
264 fn cef_extensions(&self, collector: &mut HashMap<String, String>) -> CefExtensionsResult {
265 collector.insert("customField".to_owned(), "customValue".to_owned());
266 Ok(())
267 }
268 }
269
270 #[test]
271 fn test_impl_works() {
272 let example = GoodExample {};
273 let result = example.to_cef();
274 assert!(result.is_ok());
275 assert_eq!(result.unwrap(), "CEF:0|polyverse|zerotect|V1|LinuxKernelTrap|Linux Kernel Trap|10|customField1=customValue1 customField2=customValue2 customField3=customValue2 customField4=customValue3");
276 }
277
278 #[test]
279 fn test_error_propagates() {
280 let example = BadExample {};
281 let result = example.to_cef();
282 assert!(result.is_err());
283 assert_eq!(
284 result.unwrap_err(),
285 CefConversionError::Unexpected("This error should propagate".to_owned())
286 );
287 }
288
289 #[test]
290 fn test_ext_for_datetime() {
291 let mut collector = HashMap::<String, String>::new();
292 let example = OffsetDateTime::from_unix_timestamp_nanos(3435315515325000000).unwrap();
293 let result = example.cef_extensions(&mut collector);
294 assert!(result.is_ok());
295
296 let maybe_rt = collector.get("rt");
297 assert!(maybe_rt.is_some());
298
299 let rt = maybe_rt.unwrap();
300 assert_eq!(rt, "3435315515325");
301 }
302}