Skip to main content

ic_host_artifacts/artifact/matching/
mod.rs

1//! Compare a producer's complete byte stream without retaining another copy.
2
3use std::io::{self, Write};
4
5/// A write sink that compares offered bytes with a caller-owned byte slice.
6///
7/// Mismatches are sticky but writes continue successfully so producer errors
8/// are still observed. The caller must check that production succeeded before
9/// accepting [`Self::is_complete_match`]. This chooses no encoding, schema,
10/// canonical ordering, digest or publication policy.
11pub struct MatchingWriter<'a> {
12    expected: &'a [u8],
13    position: usize,
14    matches: bool,
15}
16
17impl<'a> MatchingWriter<'a> {
18    /// Borrow the complete expected output.
19    #[must_use]
20    pub const fn new(expected: &'a [u8]) -> Self {
21        Self {
22            expected,
23            position: 0,
24            matches: true,
25        }
26    }
27
28    /// Whether all bytes written so far exactly equal the complete expected slice.
29    ///
30    /// Does not assert that the producer finished successfully.
31    #[must_use]
32    pub const fn is_complete_match(&self) -> bool {
33        self.matches && self.position == self.expected.len()
34    }
35}
36
37impl Write for MatchingWriter<'_> {
38    fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
39        let end = self.position.checked_add(buffer.len()).ok_or_else(|| {
40            io::Error::new(io::ErrorKind::InvalidData, "byte position exceeds usize")
41        })?;
42        if self.expected.get(self.position..end) != Some(buffer) {
43            self.matches = false;
44        }
45        self.position = end;
46        Ok(buffer.len())
47    }
48
49    fn flush(&mut self) -> io::Result<()> {
50        Ok(())
51    }
52}
53
54#[cfg(test)]
55mod tests;