Skip to main content

moqtap_trace/
reader.rs

1use std::io::Read;
2
3use ciborium::Value;
4
5use crate::error::MoqTraceError;
6use crate::event::TraceEvent;
7use crate::header::TraceHeader;
8use crate::writer::{MOQTRACE_MAGIC, MOQTRACE_VERSION};
9
10/// Streaming reader for `.moqtrace` files.
11///
12/// Validates the preamble and parses the header on construction. Events
13/// are then read one at a time via [`read_event`](Self::read_event) or
14/// by iterating with [`into_iter`](Self::into_iter).
15#[derive(Debug)]
16pub struct MoqTraceReader<R: Read> {
17    inner: R,
18    header: TraceHeader,
19}
20
21impl<R: Read> MoqTraceReader<R> {
22    /// Open a reader, validating the preamble and parsing the header.
23    pub fn new(mut reader: R) -> Result<Self, MoqTraceError> {
24        // 1. Magic bytes
25        let mut magic = [0u8; 8];
26        reader.read_exact(&mut magic)?;
27        if &magic != MOQTRACE_MAGIC {
28            return Err(MoqTraceError::InvalidMagic);
29        }
30
31        // 2. Format version
32        let mut version_bytes = [0u8; 4];
33        reader.read_exact(&mut version_bytes)?;
34        let version = u32::from_le_bytes(version_bytes);
35        if version != MOQTRACE_VERSION {
36            return Err(MoqTraceError::UnsupportedVersion(version));
37        }
38
39        // 3. Header length
40        let mut len_bytes = [0u8; 4];
41        reader.read_exact(&mut len_bytes)?;
42        let header_len = u32::from_le_bytes(len_bytes) as usize;
43
44        // 4. Read header CBOR bytes
45        let mut header_bytes = vec![0u8; header_len];
46        reader.read_exact(&mut header_bytes)?;
47
48        let header_value: Value = ciborium::from_reader(&header_bytes[..])?;
49        let header = TraceHeader::try_from(header_value)?;
50
51        Ok(Self { inner: reader, header })
52    }
53
54    /// Return a reference to the parsed header.
55    pub fn header(&self) -> &TraceHeader {
56        &self.header
57    }
58
59    /// Read the next event from the stream.
60    ///
61    /// Returns `Ok(None)` at EOF.
62    pub fn read_event(&mut self) -> Result<Option<TraceEvent>, MoqTraceError> {
63        match ciborium::from_reader::<Value, _>(&mut self.inner) {
64            Ok(value) => {
65                let event = TraceEvent::try_from(value)?;
66                Ok(Some(event))
67            }
68            Err(ciborium::de::Error::Io(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
69                Ok(None)
70            }
71            Err(e) => Err(MoqTraceError::CborDecode(e.to_string())),
72        }
73    }
74}
75
76impl<R: Read> IntoIterator for MoqTraceReader<R> {
77    type Item = Result<TraceEvent, MoqTraceError>;
78    type IntoIter = MoqTraceIterator<R>;
79
80    fn into_iter(self) -> Self::IntoIter {
81        MoqTraceIterator { reader: self }
82    }
83}
84
85/// Iterator over events in a `.moqtrace` file.
86pub struct MoqTraceIterator<R: Read> {
87    reader: MoqTraceReader<R>,
88}
89
90impl<R: Read> Iterator for MoqTraceIterator<R> {
91    type Item = Result<TraceEvent, MoqTraceError>;
92
93    fn next(&mut self) -> Option<Self::Item> {
94        match self.reader.read_event() {
95            Ok(Some(event)) => Some(Ok(event)),
96            Ok(None) => None,
97            Err(e) => Some(Err(e)),
98        }
99    }
100}