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 /// ```
126 /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
127 ///
128 /// let mut buf = Vec::new();
129 /// let mut sw = SequenceWriter::new(&mut buf, EncodeFormat::Hex);
130 /// sw.write_item(&Value::from(1)).unwrap();
131 /// sw.write_item(&Value::from(2)).unwrap();
132 /// assert_eq!(buf, b"0102");
133 /// ```
134 pub fn write_item(&mut self, value: &Value<'_>) -> io::Result<()> {
135 match self.format {
136 EncodeFormat::Binary => value.write_to(&mut self.writer)?,
137 EncodeFormat::Hex => value.write_hex_to(&mut self.writer)?,
138 EncodeFormat::Diagnostic => {
139 if self.wrote_any {
140 self.writer.write_all(b", ")?;
141 }
142 write!(self.writer, "{value:?}")?;
143 }
144 EncodeFormat::DiagnosticPretty => {
145 if self.wrote_any {
146 self.writer.write_all(b",\n")?;
147 }
148 write!(self.writer, "{value:#?}")?;
149 }
150 }
151 self.wrote_any = true;
152 Ok(())
153 }
154
155 /// Write every item produced by `items`. Equivalent to calling
156 /// [`write_item`](Self::write_item) in a loop, but keeps the call
157 /// site concise.
158 ///
159 /// ```
160 /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
161 ///
162 /// let items = [Value::from(1), Value::from(2), Value::from(3)];
163 /// let mut buf = Vec::new();
164 /// SequenceWriter::new(&mut buf, EncodeFormat::Binary)
165 /// .write_items(items.iter())
166 /// .unwrap();
167 /// assert_eq!(buf, [0x01, 0x02, 0x03]);
168 /// ```
169 pub fn write_items<'a, 'b, I>(&mut self, items: I) -> io::Result<()>
170 where
171 'a: 'b,
172 I: IntoIterator<Item = &'b Value<'a>>,
173 {
174 for item in items {
175 self.write_item(item)?;
176 }
177 Ok(())
178 }
179
180 /// Write each key/value pair as two consecutive items of the
181 /// sequence. Useful for emitting the contents of a map as a flat
182 /// CBOR sequence, mirroring [`Map::from_sequence`](crate::Map::from_sequence)
183 /// on the read side.
184 ///
185 /// The iterator yields borrowed references, which is the natural
186 /// shape of `&BTreeMap<Value, Value>::iter()`, so a map held in a
187 /// `Value` can be streamed directly:
188 ///
189 /// ```
190 /// use cbor_core::{SequenceWriter, Value, EncodeFormat, map};
191 ///
192 /// let value = map! { "a" => 1, "b" => 2 };
193 /// let mut sw = SequenceWriter::new(Vec::new(), EncodeFormat::Diagnostic);
194 /// sw.write_pairs(value.as_map().unwrap()).unwrap();
195 /// assert_eq!(sw.into_inner(), br#""a", 1, "b", 2"#);
196 /// ```
197 pub fn write_pairs<'a, 'b, I>(&mut self, pairs: I) -> io::Result<()>
198 where
199 'a: 'b,
200 I: IntoIterator<Item = (&'b Value<'a>, &'b Value<'a>)>,
201 {
202 for (k, v) in pairs {
203 self.write_item(k)?;
204 self.write_item(v)?;
205 }
206 Ok(())
207 }
208
209 /// Borrow the wrapped writer.
210 pub const fn get_ref(&self) -> &W {
211 &self.writer
212 }
213
214 /// Mutably borrow the wrapped writer. Direct writes bypass the
215 /// sequence writer's separator bookkeeping, so use this only for
216 /// format-specific ornamentation (for example, injecting a comment
217 /// into diagnostic output) between full items.
218 pub const fn get_mut(&mut self) -> &mut W {
219 &mut self.writer
220 }
221
222 /// Consume the sequence writer and return the wrapped writer.
223 ///
224 /// ```
225 /// use cbor_core::{SequenceWriter, Value, EncodeFormat};
226 ///
227 /// let mut sw = SequenceWriter::new(Vec::new(), EncodeFormat::Binary);
228 /// sw.write_item(&Value::from(1)).unwrap();
229 /// sw.write_item(&Value::from(2)).unwrap();
230 /// let buf = sw.into_inner();
231 /// assert_eq!(buf, [0x01, 0x02]);
232 /// ```
233 pub fn into_inner(self) -> W {
234 self.writer
235 }
236}