1#[derive(Clone, Debug)]
2#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
3#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
4#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
5#[cfg_attr(feature = "serde", serde(tag = "event"))]
6pub enum Event {
7 DiscoverStart(DiscoverStart),
8 DiscoverCase(DiscoverCase),
9 DiscoverComplete(DiscoverComplete),
10 RunStart(RunStart),
11 CaseStart(CaseStart),
12 CaseMessage(CaseMessage),
13 CaseComplete(CaseComplete),
14 RunComplete(RunComplete),
15}
16
17impl Event {
18 #[cfg(feature = "json")]
19 pub fn to_jsonline(&self) -> String {
20 match self {
21 Self::DiscoverStart(event) => event.to_jsonline(),
22 Self::DiscoverCase(event) => event.to_jsonline(),
23 Self::DiscoverComplete(event) => event.to_jsonline(),
24 Self::RunStart(event) => event.to_jsonline(),
25 Self::CaseStart(event) => event.to_jsonline(),
26 Self::CaseMessage(event) => event.to_jsonline(),
27 Self::CaseComplete(event) => event.to_jsonline(),
28 Self::RunComplete(event) => event.to_jsonline(),
29 }
30 }
31}
32
33impl From<DiscoverStart> for Event {
34 fn from(inner: DiscoverStart) -> Self {
35 Self::DiscoverStart(inner)
36 }
37}
38
39impl From<DiscoverCase> for Event {
40 fn from(inner: DiscoverCase) -> Self {
41 Self::DiscoverCase(inner)
42 }
43}
44
45impl From<DiscoverComplete> for Event {
46 fn from(inner: DiscoverComplete) -> Self {
47 Self::DiscoverComplete(inner)
48 }
49}
50
51impl From<RunStart> for Event {
52 fn from(inner: RunStart) -> Self {
53 Self::RunStart(inner)
54 }
55}
56
57impl From<CaseStart> for Event {
58 fn from(inner: CaseStart) -> Self {
59 Self::CaseStart(inner)
60 }
61}
62
63impl From<CaseMessage> for Event {
64 fn from(inner: CaseMessage) -> Self {
65 Self::CaseMessage(inner)
66 }
67}
68
69impl From<CaseComplete> for Event {
70 fn from(inner: CaseComplete) -> Self {
71 Self::CaseComplete(inner)
72 }
73}
74
75impl From<RunComplete> for Event {
76 fn from(inner: RunComplete) -> Self {
77 Self::RunComplete(inner)
78 }
79}
80
81#[derive(Clone, Debug)]
82#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
83#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
85pub struct DiscoverStart {
86 #[cfg_attr(
87 feature = "serde",
88 serde(default, skip_serializing_if = "Option::is_none")
89 )]
90 pub elapsed_s: Option<Elapsed>,
91}
92
93impl DiscoverStart {
94 #[cfg(feature = "json")]
95 pub fn to_jsonline(&self) -> String {
96 use json_write::JsonWrite as _;
97
98 let mut buffer = String::new();
99 buffer.open_object().unwrap();
100
101 buffer.key("event").unwrap();
102 buffer.keyval_sep().unwrap();
103 buffer.value("discover_start").unwrap();
104
105 if let Some(elapsed_s) = self.elapsed_s {
106 buffer.val_sep().unwrap();
107 buffer.key("elapsed_s").unwrap();
108 buffer.keyval_sep().unwrap();
109 buffer.value(String::from(elapsed_s)).unwrap();
110 }
111
112 buffer.close_object().unwrap();
113
114 buffer
115 }
116}
117
118#[derive(Clone, Debug)]
122#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
125pub struct DiscoverCase {
126 pub name: String,
127 #[cfg_attr(
128 feature = "serde",
129 serde(default, skip_serializing_if = "RunMode::is_default")
130 )]
131 pub mode: RunMode,
132 #[cfg_attr(
134 feature = "serde",
135 serde(default = "true_default", skip_serializing_if = "is_true")
136 )]
137 pub selected: bool,
138 #[cfg_attr(
139 feature = "serde",
140 serde(default, skip_serializing_if = "Option::is_none")
141 )]
142 pub elapsed_s: Option<Elapsed>,
143}
144
145impl DiscoverCase {
146 #[cfg(feature = "json")]
147 pub fn to_jsonline(&self) -> String {
148 use json_write::JsonWrite as _;
149
150 let mut buffer = String::new();
151 buffer.open_object().unwrap();
152
153 buffer.key("event").unwrap();
154 buffer.keyval_sep().unwrap();
155 buffer.value("discover_case").unwrap();
156
157 buffer.val_sep().unwrap();
158 buffer.key("name").unwrap();
159 buffer.keyval_sep().unwrap();
160 buffer.value(&self.name).unwrap();
161
162 if !self.mode.is_default() {
163 buffer.val_sep().unwrap();
164 buffer.key("mode").unwrap();
165 buffer.keyval_sep().unwrap();
166 buffer.value(self.mode.as_str()).unwrap();
167 }
168
169 if !self.selected {
170 buffer.val_sep().unwrap();
171 buffer.key("selected").unwrap();
172 buffer.keyval_sep().unwrap();
173 buffer.value(self.selected).unwrap();
174 }
175
176 if let Some(elapsed_s) = self.elapsed_s {
177 buffer.val_sep().unwrap();
178 buffer.key("elapsed_s").unwrap();
179 buffer.keyval_sep().unwrap();
180 buffer.value(String::from(elapsed_s)).unwrap();
181 }
182
183 buffer.close_object().unwrap();
184
185 buffer
186 }
187}
188
189#[derive(Clone, Debug)]
190#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
191#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
192#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
193pub struct DiscoverComplete {
194 #[cfg_attr(
195 feature = "serde",
196 serde(default, skip_serializing_if = "Option::is_none")
197 )]
198 pub elapsed_s: Option<Elapsed>,
199}
200
201impl DiscoverComplete {
202 #[cfg(feature = "json")]
203 pub fn to_jsonline(&self) -> String {
204 use json_write::JsonWrite as _;
205
206 let mut buffer = String::new();
207 buffer.open_object().unwrap();
208
209 buffer.key("event").unwrap();
210 buffer.keyval_sep().unwrap();
211 buffer.value("discover_complete").unwrap();
212
213 if let Some(elapsed_s) = self.elapsed_s {
214 buffer.val_sep().unwrap();
215 buffer.key("elapsed_s").unwrap();
216 buffer.keyval_sep().unwrap();
217 buffer.value(String::from(elapsed_s)).unwrap();
218 }
219
220 buffer.close_object().unwrap();
221
222 buffer
223 }
224}
225
226#[derive(Clone, Debug)]
227#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
228#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
229#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
230pub struct RunStart {
231 #[cfg_attr(
232 feature = "serde",
233 serde(default, skip_serializing_if = "Option::is_none")
234 )]
235 pub elapsed_s: Option<Elapsed>,
236}
237
238impl RunStart {
239 #[cfg(feature = "json")]
240 pub fn to_jsonline(&self) -> String {
241 use json_write::JsonWrite as _;
242
243 let mut buffer = String::new();
244 buffer.open_object().unwrap();
245
246 buffer.key("event").unwrap();
247 buffer.keyval_sep().unwrap();
248 buffer.value("run_start").unwrap();
249
250 if let Some(elapsed_s) = self.elapsed_s {
251 buffer.val_sep().unwrap();
252 buffer.key("elapsed_s").unwrap();
253 buffer.keyval_sep().unwrap();
254 buffer.value(String::from(elapsed_s)).unwrap();
255 }
256
257 buffer.close_object().unwrap();
258
259 buffer
260 }
261}
262
263#[derive(Clone, Debug)]
264#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
265#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
266#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
267pub struct CaseStart {
268 pub name: String,
269 #[cfg_attr(
270 feature = "serde",
271 serde(default, skip_serializing_if = "Option::is_none")
272 )]
273 pub elapsed_s: Option<Elapsed>,
274}
275
276impl CaseStart {
277 #[cfg(feature = "json")]
278 pub fn to_jsonline(&self) -> String {
279 use json_write::JsonWrite as _;
280
281 let mut buffer = String::new();
282 buffer.open_object().unwrap();
283
284 buffer.key("event").unwrap();
285 buffer.keyval_sep().unwrap();
286 buffer.value("case_start").unwrap();
287
288 buffer.val_sep().unwrap();
289 buffer.key("name").unwrap();
290 buffer.keyval_sep().unwrap();
291 buffer.value(&self.name).unwrap();
292
293 if let Some(elapsed_s) = self.elapsed_s {
294 buffer.val_sep().unwrap();
295 buffer.key("elapsed_s").unwrap();
296 buffer.keyval_sep().unwrap();
297 buffer.value(String::from(elapsed_s)).unwrap();
298 }
299
300 buffer.close_object().unwrap();
301
302 buffer
303 }
304}
305
306#[derive(Clone, Debug)]
307#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
310pub struct CaseMessage {
311 pub name: String,
312 pub kind: MessageKind,
313 #[cfg_attr(
314 feature = "serde",
315 serde(default, skip_serializing_if = "Option::is_none")
316 )]
317 pub message: Option<String>,
318 #[cfg_attr(
319 feature = "serde",
320 serde(default, skip_serializing_if = "Option::is_none")
321 )]
322 pub elapsed_s: Option<Elapsed>,
323}
324
325impl CaseMessage {
326 #[cfg(feature = "json")]
327 pub fn to_jsonline(&self) -> String {
328 use json_write::JsonWrite as _;
329
330 let mut buffer = String::new();
331 buffer.open_object().unwrap();
332
333 buffer.key("event").unwrap();
334 buffer.keyval_sep().unwrap();
335 buffer.value("case_message").unwrap();
336
337 buffer.val_sep().unwrap();
338 buffer.key("name").unwrap();
339 buffer.keyval_sep().unwrap();
340 buffer.value(&self.name).unwrap();
341
342 buffer.val_sep().unwrap();
343 buffer.key("kind").unwrap();
344 buffer.keyval_sep().unwrap();
345 buffer.value(self.kind.as_str()).unwrap();
346
347 if let Some(message) = &self.message {
348 buffer.val_sep().unwrap();
349 buffer.key("message").unwrap();
350 buffer.keyval_sep().unwrap();
351 buffer.value(message).unwrap();
352 }
353
354 if let Some(elapsed_s) = self.elapsed_s {
355 buffer.val_sep().unwrap();
356 buffer.key("elapsed_s").unwrap();
357 buffer.keyval_sep().unwrap();
358 buffer.value(String::from(elapsed_s)).unwrap();
359 }
360
361 buffer.close_object().unwrap();
362
363 buffer
364 }
365}
366
367#[derive(Clone, Debug)]
368#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
369#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
370#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
371pub struct CaseComplete {
372 pub name: String,
373 #[cfg_attr(
374 feature = "serde",
375 serde(default, skip_serializing_if = "Option::is_none")
376 )]
377 pub elapsed_s: Option<Elapsed>,
378}
379
380impl CaseComplete {
381 #[cfg(feature = "json")]
382 pub fn to_jsonline(&self) -> String {
383 use json_write::JsonWrite as _;
384
385 let mut buffer = String::new();
386 buffer.open_object().unwrap();
387
388 buffer.key("event").unwrap();
389 buffer.keyval_sep().unwrap();
390 buffer.value("case_complete").unwrap();
391
392 buffer.val_sep().unwrap();
393 buffer.key("name").unwrap();
394 buffer.keyval_sep().unwrap();
395 buffer.value(&self.name).unwrap();
396
397 if let Some(elapsed_s) = self.elapsed_s {
398 buffer.val_sep().unwrap();
399 buffer.key("elapsed_s").unwrap();
400 buffer.keyval_sep().unwrap();
401 buffer.value(String::from(elapsed_s)).unwrap();
402 }
403
404 buffer.close_object().unwrap();
405
406 buffer
407 }
408}
409
410#[derive(Clone, Debug)]
411#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
412#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
413#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
414pub struct RunComplete {
415 #[cfg_attr(
416 feature = "serde",
417 serde(default, skip_serializing_if = "Option::is_none")
418 )]
419 pub elapsed_s: Option<Elapsed>,
420}
421
422impl RunComplete {
423 #[cfg(feature = "json")]
424 pub fn to_jsonline(&self) -> String {
425 use json_write::JsonWrite as _;
426
427 let mut buffer = String::new();
428 buffer.open_object().unwrap();
429
430 buffer.key("event").unwrap();
431 buffer.keyval_sep().unwrap();
432 buffer.value("run_complete").unwrap();
433
434 if let Some(elapsed_s) = self.elapsed_s {
435 buffer.val_sep().unwrap();
436 buffer.key("elapsed_s").unwrap();
437 buffer.keyval_sep().unwrap();
438 buffer.value(String::from(elapsed_s)).unwrap();
439 }
440
441 buffer.close_object().unwrap();
442
443 buffer
444 }
445}
446
447#[cfg(feature = "serde")]
448fn true_default() -> bool {
449 true
450}
451
452#[cfg(feature = "serde")]
453fn is_true(yes: &bool) -> bool {
454 *yes
455}
456
457#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
458#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
459#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
460#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
461pub enum RunMode {
462 #[default]
463 Test,
464 Bench,
465}
466
467impl RunMode {
468 pub fn as_str(&self) -> &str {
469 match self {
470 Self::Test => "test",
471 Self::Bench => "bench",
472 }
473 }
474
475 #[cfg(any(feature = "serde", feature = "json"))]
476 fn is_default(&self) -> bool {
477 *self == Default::default()
478 }
479}
480
481#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
482#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
483#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
484#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
485pub enum MessageKind {
486 Error,
488 Ignored,
489}
490
491impl MessageKind {
492 pub fn as_str(&self) -> &str {
493 match self {
494 Self::Error => "error",
495 Self::Ignored => "ignored",
496 }
497 }
498}
499
500#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
502#[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))]
503#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
504#[cfg_attr(feature = "serde", serde(into = "String"))]
505#[cfg_attr(feature = "serde", serde(try_from = "String"))]
506pub struct Elapsed(pub std::time::Duration);
507
508impl std::fmt::Display for Elapsed {
509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510 write!(f, "{:.3}s", self.0.as_secs_f64())
511 }
512}
513
514impl std::str::FromStr for Elapsed {
515 type Err = std::num::ParseFloatError;
516
517 fn from_str(src: &str) -> Result<Self, Self::Err> {
518 let secs = src.parse()?;
519 Ok(Elapsed(std::time::Duration::from_secs_f64(secs)))
520 }
521}
522
523impl TryFrom<String> for Elapsed {
524 type Error = std::num::ParseFloatError;
525
526 fn try_from(inner: String) -> Result<Self, Self::Error> {
527 inner.parse()
528 }
529}
530
531impl From<Elapsed> for String {
532 fn from(elapsed: Elapsed) -> Self {
533 elapsed.0.as_secs_f64().to_string()
534 }
535}
536
537#[cfg(feature = "unstable-schema")]
538#[test]
539fn dump_event_schema() {
540 let schema = schemars::schema_for!(Event);
541 let dump = serde_json::to_string_pretty(&schema).unwrap();
542 snapbox::assert_data_eq!(dump, snapbox::file!("../event.schema.json").raw());
543}