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}