Skip to main content

cbor_cli/
import.rs

1use std::io;
2
3use cbor2::de::Deserializer;
4use cbor2::value::Value as CborValue;
5
6/// Convert a stream of some other format into a stream of CBOR bytes.
7///
8/// Each top-level item in the input is serialized to CBOR and written to
9/// `writer`. The input is never buffered in memory — items are streamed one at
10/// a time.
11///
12/// # Arguments
13///
14/// - `input_format`: one of `"json"`, `"yaml"`, `"toml"`, `"cbor"`. A `"cbor"`
15///   input is re-emitted item-for-item (passthrough/normalization).
16/// - `reader`: the input byte stream. Multiple concatenated top-level items
17///   are supported for every format that allows it (all except yaml/toml,
18///   which consume the whole stream as one value).
19/// - `writer`: the CBOR output sink.
20///
21/// # Example
22///
23/// ```
24/// use cbor_cli::import::import_from_reader;
25///
26/// let json = br#"{"key": "value"}"#;
27/// let mut cbor = Vec::new();
28/// import_from_reader("json", &json[..], &mut cbor).unwrap();
29/// assert_eq!(cbor[0], 0xa1); // CBOR map header (major type 5, length 1)
30/// ```
31pub fn import_from_reader<R: io::Read, W: io::Write>(
32  input_format: &str,
33  mut reader: R,
34  writer: &mut W,
35) -> anyhow::Result<()> {
36  match input_format {
37    "json" => {
38      for v in serde_json::de::Deserializer::from_reader(reader).into_iter::<serde_json::Value>() {
39        let v = v?;
40        cbor2::to_writer(&v, &mut *writer)?;
41      }
42    }
43    "yaml" => {
44      let result: serde_yaml::Value = serde_yaml::from_reader(reader)?;
45      cbor2::to_writer(&result, writer)?;
46    }
47    "toml" => {
48      let mut s = String::new();
49      reader.read_to_string(&mut s)?;
50      let result: toml::Value = toml::de::from_str(&s)?;
51      cbor2::to_writer(&result, writer)?;
52    }
53    _ => {
54      // CBOR passthrough: decode each consecutive top-level item and re-emit it.
55      for v in Deserializer::from_reader(reader).into_iter::<CborValue>() {
56        let v = v?;
57        cbor2::to_writer(&v, &mut *writer)?;
58      }
59    }
60  }
61  Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66  use super::*;
67
68  #[test]
69  fn test_import_array() {
70    let cases = [
71      ("json", &b"[1,2]"[..], vec![0x82, 0x01, 0x02]),
72      ("yaml", &b"- 1\n- 2\n"[..], vec![0x82, 0x01, 0x02]),
73      ("cbor", &b"\x82\x01\x02"[..], vec![0x82, 0x01, 0x02]),
74    ];
75    for (format, input, expected) in cases {
76      let mut output = Vec::new();
77      import_from_reader(format, input, &mut output).unwrap();
78      assert_eq!(output, expected, "format: {format}");
79    }
80  }
81
82  #[test]
83  fn test_import_map_from_toml() {
84    let input = b"foo = 1\nbar = 2\n";
85    let mut output = Vec::new();
86    import_from_reader("toml", &input[..], &mut output).unwrap();
87    // Expected hex: a2 63 66 6f 6f 01 63 62 61 72 02
88    assert_eq!(output, vec![0xa2, 0x63, 0x66, 0x6f, 0x6f, 0x01, 0x63, 0x62, 0x61, 0x72, 0x02]);
89  }
90
91  #[test]
92  fn test_import_invalid_json() {
93    let input = b"not json";
94    let mut output = Vec::new();
95    let err = import_from_reader("json", &input[..], &mut output).unwrap_err();
96    assert!(!err.to_string().is_empty(), "error should be non-empty: {err}");
97  }
98}