Skip to main content

cbor_core/
encoder.rs

1//! Streaming encoder for CBOR sequences.
2//!
3//! [`SequenceWriter`] is the write-side counterpart of
4//! [`SequenceDecoder`](crate::SequenceDecoder) and
5//! [`SequenceReader`](crate::SequenceReader). It wraps any
6//! [`io::Write`](std::io::Write) and emits a CBOR sequence (RFC 8742)
7//! in the format selected by [`EncodeFormat`].
8
9use std::io::{self, Write};
10
11use crate::{EncodeFormat, Value};
12
13/// Streaming writer for CBOR sequences in binary, hex, or diagnostic notation.
14///
15/// Construct with [`SequenceWriter::new`], call
16/// [`write_item`](Self::write_item), [`write_items`](Self::write_items),
17/// or [`write_pairs`](Self::write_pairs) to emit content, and then
18/// [`into_inner`](Self::into_inner) to get the wrapped writer back.
19///
20/// Format semantics:
21///
22/// * [`EncodeFormat::Binary`] and [`EncodeFormat::Hex`]: items are
23///   concatenated with no separator. Each item's bytes form a
24///   self-delimiting CBOR value.
25/// * [`EncodeFormat::Diagnostic`]: items are separated by `, ` (comma
26///   and a space). The first item is written without a leading
27///   separator; no trailing comma is emitted.
28/// * [`EncodeFormat::DiagnosticPretty`]: items are pretty-printed with
29///   `{:#?}` (indented, multi-line for collections) and separated by
30///   `,\n`.
31///
32/// [`Format`](crate::Format) values accepted on the read side pass
33/// through the `impl Into<EncodeFormat>` bound unchanged.
34///
35/// # Examples
36///
37/// Binary sequence:
38///
39/// ```
40/// use cbor_core::{SequenceWriter, Value, EncodeFormat};
41///
42/// let mut buf = Vec::new();
43/// let mut sw = SequenceWriter::new(&mut buf, EncodeFormat::Binary);
44/// sw.write_item(&Value::from(1)).unwrap();
45/// sw.write_item(&Value::from(2)).unwrap();
46/// sw.write_item(&Value::from(3)).unwrap();
47/// assert_eq!(buf, [0x01, 0x02, 0x03]);
48/// ```
49///
50/// Diagnostic sequence, with separators inserted automatically:
51///
52/// ```
53/// use cbor_core::{SequenceWriter, Value, EncodeFormat};
54///
55/// let mut buf = Vec::new();
56/// let mut sw = SequenceWriter::new(&mut buf, EncodeFormat::Diagnostic);
57/// sw.write_items([Value::from(1), Value::from("hi"), Value::from(true)].iter()).unwrap();
58/// assert_eq!(String::from_utf8(buf).unwrap(), r#"1, "hi", true"#);
59/// ```
60///
61/// A [`Format`](crate::Format) value (from the read-side API) converts
62/// implicitly:
63///
64/// ```
65/// use cbor_core::{Format, SequenceWriter, Value};
66///
67/// let mut buf = Vec::new();
68/// let mut sw = SequenceWriter::new(&mut buf, Format::Hex);
69/// sw.write_item(&Value::from(1)).unwrap();
70/// assert_eq!(buf, b"01");
71/// ```
72///
73/// Round-trip through [`SequenceDecoder`](crate::SequenceDecoder):
74///
75/// ```
76/// use cbor_core::{Array, DecodeOptions, Format, SequenceWriter, Value, EncodeFormat};
77///
78/// let items = [Value::from(1), Value::from("hi"), Value::from(true)];
79///
80/// let mut buf = Vec::new();
81/// let mut sw = SequenceWriter::new(&mut buf, EncodeFormat::Diagnostic);
82/// sw.write_items(items.iter()).unwrap();
83///
84/// let decoded = Array::try_from_sequence(
85///     DecodeOptions::new().format(Format::Diagnostic).sequence_decoder(&buf),
86/// ).unwrap();
87/// assert_eq!(decoded.get_ref().as_slice(), &items);
88/// ```
89pub struct SequenceWriter<W: Write> {
90    writer: W,
91    format: EncodeFormat,
92    wrote_any: bool,
93}
94
95impl<W: Write> SequenceWriter<W> {
96    /// Create a new sequence writer wrapping `writer` and emitting
97    /// items in the selected `format`. Accepts any
98    /// `impl Into<EncodeFormat>`, so [`Format`](crate::Format) values
99    /// pass through unchanged.
100    ///
101    /// ```
102    /// use cbor_core::{SequenceWriter, EncodeFormat};
103    ///
104    /// let sw = SequenceWriter::new(Vec::new(), EncodeFormat::Hex);
105    /// // `sw` now accepts items via `write_item` / `write_items` / `write_pairs`.
106    /// # drop(sw);
107    /// ```
108    pub fn new(writer: W, format: impl Into<EncodeFormat>) -> Self {
109        Self {
110            writer,
111            format: format.into(),
112            wrote_any: false,
113        }
114    }
115
116    /// Write one item of the sequence.
117    ///
118    /// In [`EncodeFormat::Diagnostic`] a `, ` separator is inserted
119    /// before every item except the first. In
120    /// [`EncodeFormat::DiagnosticPretty`] the separator is `,\n` and
121    /// each item is formatted with `{:#?}`. In [`EncodeFormat::Binary`]
122    /// and [`EncodeFormat::Hex`] items are written back-to-back with no
123    /// separator.
124    ///
125    /// # Errors
126    ///
127    /// Returns any [`io::Error`] reported by the underlying writer.
128    ///
129    /// ```
130    /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
131    ///
132    /// let mut buf = Vec::new();
133    /// let mut sw = SequenceWriter::new(&mut buf, EncodeFormat::Hex);
134    /// sw.write_item(&Value::from(1)).unwrap();
135    /// sw.write_item(&Value::from(2)).unwrap();
136    /// assert_eq!(buf, b"0102");
137    /// ```
138    pub fn write_item(&mut self, value: &Value<'_>) -> io::Result<()> {
139        match self.format {
140            EncodeFormat::Binary => value.write_to(&mut self.writer)?,
141            EncodeFormat::Hex => value.write_hex_to(&mut self.writer)?,
142            EncodeFormat::Diagnostic => {
143                if self.wrote_any {
144                    self.writer.write_all(b", ")?;
145                }
146                write!(self.writer, "{value:?}")?;
147            }
148            EncodeFormat::DiagnosticPretty => {
149                if self.wrote_any {
150                    self.writer.write_all(b",\n")?;
151                }
152                write!(self.writer, "{value:#?}")?;
153            }
154        }
155        self.wrote_any = true;
156        Ok(())
157    }
158
159    /// Write every item produced by `items`. Equivalent to calling
160    /// [`write_item`](Self::write_item) in a loop, but keeps the call
161    /// site concise.
162    ///
163    /// # Errors
164    ///
165    /// Stops at and returns the first [`io::Error`] reported by the
166    /// underlying writer.
167    ///
168    /// ```
169    /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
170    ///
171    /// let items = [Value::from(1), Value::from(2), Value::from(3)];
172    /// let mut buf = Vec::new();
173    /// SequenceWriter::new(&mut buf, EncodeFormat::Binary)
174    ///     .write_items(items.iter())
175    ///     .unwrap();
176    /// assert_eq!(buf, [0x01, 0x02, 0x03]);
177    /// ```
178    pub fn write_items<'a, 'b, I>(&mut self, items: I) -> io::Result<()>
179    where
180        'a: 'b,
181        I: IntoIterator<Item = &'b Value<'a>>,
182    {
183        for item in items {
184            self.write_item(item)?;
185        }
186        Ok(())
187    }
188
189    /// Write each key/value pair as two consecutive items of the
190    /// sequence. Useful for emitting the contents of a map as a flat
191    /// CBOR sequence, mirroring [`Map::from_sequence`](crate::Map::from_sequence)
192    /// on the read side.
193    ///
194    /// The iterator yields borrowed references, which is the natural
195    /// shape of `&BTreeMap<Value, Value>::iter()`, so a map held in a
196    /// `Value` can be streamed directly.
197    ///
198    /// # Errors
199    ///
200    /// Stops at and returns the first [`io::Error`] reported by the
201    /// underlying writer.
202    ///
203    /// ```
204    /// use cbor_core::{SequenceWriter, Value, EncodeFormat, map};
205    ///
206    /// let value = map! { "a" => 1, "b" => 2 };
207    /// let mut sw = SequenceWriter::new(Vec::new(), EncodeFormat::Diagnostic);
208    /// sw.write_pairs(value.as_map().unwrap()).unwrap();
209    /// assert_eq!(sw.into_inner(), br#""a", 1, "b", 2"#);
210    /// ```
211    pub fn write_pairs<'a, 'b, I>(&mut self, pairs: I) -> io::Result<()>
212    where
213        'a: 'b,
214        I: IntoIterator<Item = (&'b Value<'a>, &'b Value<'a>)>,
215    {
216        for (k, v) in pairs {
217            self.write_item(k)?;
218            self.write_item(v)?;
219        }
220        Ok(())
221    }
222
223    /// Borrow the wrapped writer.
224    pub const fn get_ref(&self) -> &W {
225        &self.writer
226    }
227
228    /// Mutably borrow the wrapped writer. Direct writes bypass the
229    /// sequence writer's separator bookkeeping, so use this only for
230    /// format-specific ornamentation (for example, injecting a comment
231    /// into diagnostic output) between full items.
232    pub const fn get_mut(&mut self) -> &mut W {
233        &mut self.writer
234    }
235
236    /// Consume the sequence writer and return the wrapped writer.
237    ///
238    /// ```
239    /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
240    ///
241    /// let mut sw = SequenceWriter::new(Vec::new(), EncodeFormat::Binary);
242    /// sw.write_item(&Value::from(1)).unwrap();
243    /// sw.write_item(&Value::from(2)).unwrap();
244    /// let buf = sw.into_inner();
245    /// assert_eq!(buf, [0x01, 0x02]);
246    /// ```
247    pub fn into_inner(self) -> W {
248        self.writer
249    }
250}