qlog/reader.rs
1// Copyright (C) 2023, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10//
11// * Redistributions in binary form must reproduce the above copyright
12// notice, this list of conditions and the following disclaimer in the
13// documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27use crate::QlogSeq;
28
29/// Represents the format of the read event.
30#[allow(clippy::large_enum_variant)]
31pub enum Event {
32 /// A native qlog event type.
33 Qlog(crate::events::Event),
34
35 // An extended JSON event type.
36 Json(crate::events::JsonEvent),
37}
38
39/// A helper object specialized for reading JSON-SEQ qlog from a [`BufRead`]
40/// trait.
41///
42/// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html
43pub struct QlogSeqReader<'a> {
44 pub qlog: QlogSeq,
45 reader: Box<dyn std::io::BufRead + Send + Sync + 'a>,
46}
47
48impl<'a> QlogSeqReader<'a> {
49 pub fn new(
50 mut reader: Box<dyn std::io::BufRead + Send + Sync + 'a>,
51 ) -> Result<Self, Box<dyn std::error::Error>> {
52 // "null record" skip it
53 Self::read_record(reader.as_mut());
54
55 let header = Self::read_record(reader.as_mut()).ok_or_else(|| {
56 std::io::Error::new(
57 std::io::ErrorKind::Other,
58 "error reading file header bytes",
59 )
60 })?;
61
62 let res: Result<QlogSeq, serde_json::Error> =
63 serde_json::from_slice(&header);
64 match res {
65 Ok(qlog) => Ok(Self { qlog, reader }),
66
67 Err(e) => Err(e.into()),
68 }
69 }
70
71 fn read_record(
72 reader: &mut (dyn std::io::BufRead + Send + Sync),
73 ) -> Option<Vec<u8>> {
74 let mut buf = Vec::<u8>::new();
75 let size = reader.read_until(b'', &mut buf).unwrap();
76 if size <= 1 {
77 return None;
78 }
79
80 buf.truncate(buf.len() - 1);
81
82 Some(buf)
83 }
84}
85
86impl Iterator for QlogSeqReader<'_> {
87 type Item = Event;
88
89 #[inline]
90 fn next(&mut self) -> Option<Self::Item> {
91 // Attempt to deserialize events but skip them if that fails for any
92 // reason, ensuring we always read all bytes in the reader.
93 while let Some(bytes) = Self::read_record(&mut self.reader) {
94 let r: serde_json::Result<crate::events::Event> =
95 serde_json::from_slice(&bytes);
96
97 if let Ok(event) = r {
98 return Some(Event::Qlog(event));
99 }
100
101 let r: serde_json::Result<crate::events::JsonEvent> =
102 serde_json::from_slice(&bytes);
103
104 if let Ok(event) = r {
105 return Some(Event::Json(event));
106 }
107 }
108
109 None
110 }
111}