Skip to main content

cbor_cli/
inspect.rs

1use std::io;
2
3use cbor2::de::Deserializer;
4use cbor2::value::Value as CborValue;
5use anyhow::Context;
6
7/// Adapter that bridges `std::io::Write` to `std::fmt::Write`, so a `Display`
8/// value can be formatted straight onto an I/O sink with no intermediate
9/// `String` allocation.
10struct FmtToIo<W: io::Write>(W);
11
12impl<W: io::Write> std::fmt::Write for FmtToIo<W> {
13  fn write_str(&mut self, s: &str) -> std::fmt::Result {
14    self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
15  }
16}
17
18/// Deep inspection of CBOR files.
19///
20/// Decodes each top-level CBOR item and writes its Concise Diagnostic Notation
21/// (CDN, RFC 8949 §8) representation followed by a newline. CDN is a
22/// human-readable, standard rendering of CBOR: byte strings appear as
23/// `h'...'`, tags and bignums are rendered faithfully, and the output is more
24/// compact and standard than Rust's `{:?}` debug format.
25///
26/// Reads from `reader` in a fully streaming fashion — one item at a time — so
27/// the input is never buffered in memory. Each item's diagnostic text is
28/// written straight to `writer` with no intermediate allocation on this side
29/// of the crate boundary.
30pub fn inspect_from_reader<R: io::Read, W: io::Write>(reader: R, mut writer: W) -> anyhow::Result<()> {
31  for v in Deserializer::from_reader(reader).into_iter::<CborValue>() {
32    let v = v?;
33    use std::fmt::Write as _;
34    write!(FmtToIo(&mut writer), "{v}").context("failed to write diagnostic notation")?;
35    writer.write_all(b"\n")?;
36  }
37  Ok(())
38}
39
40#[cfg(test)]
41mod tests {
42  use super::*;
43
44  #[test]
45  fn test_inspect() {
46    let cases: &[(&[u8], &[u8])] = &[
47      (&[0x82, 0x01, 0x02], b"[1, 2]\n"),
48      (&[0xa1, 0x61, 0x61, 0x61, 0x62], b"{\"a\": \"b\"}\n"),
49      (&[0x42, 0x01, 0x02], b"h'0102'\n"),
50      (&[0x01, 0x02], b"1\n2\n"),
51    ];
52    for &(input, expected) in cases {
53      let mut output = Vec::new();
54      inspect_from_reader(input, &mut output).unwrap();
55      assert_eq!(output, expected, "input: {input:02x?}");
56    }
57  }
58}