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 [`Format`].
8
9use std::io::{self, Write};
10
11use crate::{Format, 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 mirror the reader side:
21///
22/// * [`Format::Binary`] and [`Format::Hex`]: items are concatenated with
23/// no separator. Each item's bytes form a self-delimiting CBOR value.
24/// * [`Format::Diagnostic`]: items are separated by `, ` (comma and a
25/// space). The first item is written without a leading separator; no
26/// trailing comma is emitted.
27///
28/// The writer is format-agnostic to the caller: the same code path
29/// works for all three formats, and switching format is a single
30/// constructor argument.
31///
32/// # Examples
33///
34/// Binary sequence:
35///
36/// ```
37/// use cbor_core::{Format, SequenceWriter, Value};
38///
39/// let mut buf = Vec::new();
40/// let mut sw = SequenceWriter::new(&mut buf, Format::Binary);
41/// sw.write_item(&Value::from(1)).unwrap();
42/// sw.write_item(&Value::from(2)).unwrap();
43/// sw.write_item(&Value::from(3)).unwrap();
44/// assert_eq!(buf, [0x01, 0x02, 0x03]);
45/// ```
46///
47/// Diagnostic sequence, with separators inserted automatically:
48///
49/// ```
50/// use cbor_core::{Format, SequenceWriter, Value};
51///
52/// let mut buf = Vec::new();
53/// let mut sw = SequenceWriter::new(&mut buf, Format::Diagnostic);
54/// sw.write_items([Value::from(1), Value::from("hi"), Value::from(true)].iter()).unwrap();
55/// assert_eq!(String::from_utf8(buf).unwrap(), r#"1, "hi", true"#);
56/// ```
57///
58/// Round-trip through [`SequenceDecoder`](crate::SequenceDecoder):
59///
60/// ```
61/// use cbor_core::{Array, DecodeOptions, Format, SequenceWriter, Value};
62///
63/// let items = [Value::from(1), Value::from("hi"), Value::from(true)];
64///
65/// let mut buf = Vec::new();
66/// let mut sw = SequenceWriter::new(&mut buf, Format::Diagnostic);
67/// sw.write_items(items.iter()).unwrap();
68///
69/// let decoded = Array::try_from_sequence(
70/// DecodeOptions::new().format(Format::Diagnostic).sequence_decoder(&buf),
71/// ).unwrap();
72/// assert_eq!(decoded.get_ref().as_slice(), &items);
73/// ```
74pub struct SequenceWriter<W: Write> {
75 writer: W,
76 format: Format,
77 wrote_any: bool,
78}
79
80impl<W: Write> SequenceWriter<W> {
81 /// Create a new sequence writer wrapping `writer` and emitting
82 /// items in the selected `format`.
83 ///
84 /// ```
85 /// use cbor_core::{Format, SequenceWriter};
86 ///
87 /// let sw = SequenceWriter::new(Vec::new(), Format::Hex);
88 /// // `sw` now accepts items via `write_item` / `write_items` / `write_pairs`.
89 /// # drop(sw);
90 /// ```
91 pub const fn new(writer: W, format: Format) -> Self {
92 Self {
93 writer,
94 format,
95 wrote_any: false,
96 }
97 }
98
99 /// Write one item of the sequence.
100 ///
101 /// In [`Format::Diagnostic`] a `, ` separator is inserted before
102 /// every item except the first. In [`Format::Binary`] and
103 /// [`Format::Hex`] items are written back-to-back with no separator.
104 ///
105 /// ```
106 /// use cbor_core::{Format, SequenceWriter, Value};
107 ///
108 /// let mut buf = Vec::new();
109 /// let mut sw = SequenceWriter::new(&mut buf, Format::Hex);
110 /// sw.write_item(&Value::from(1)).unwrap();
111 /// sw.write_item(&Value::from(2)).unwrap();
112 /// assert_eq!(buf, b"0102");
113 /// ```
114 pub fn write_item(&mut self, value: &Value) -> io::Result<()> {
115 match self.format {
116 Format::Binary => value.write_to(&mut self.writer)?,
117 Format::Hex => value.write_hex_to(&mut self.writer)?,
118 Format::Diagnostic => {
119 if self.wrote_any {
120 self.writer.write_all(b", ")?;
121 }
122 write!(self.writer, "{value:?}")?;
123 }
124 }
125 self.wrote_any = true;
126 Ok(())
127 }
128
129 /// Write every item produced by `items`. Equivalent to calling
130 /// [`write_item`](Self::write_item) in a loop, but keeps the call
131 /// site concise.
132 ///
133 /// ```
134 /// use cbor_core::{Format, SequenceWriter, Value};
135 ///
136 /// let items = [Value::from(1), Value::from(2), Value::from(3)];
137 /// let mut buf = Vec::new();
138 /// SequenceWriter::new(&mut buf, Format::Binary)
139 /// .write_items(items.iter())
140 /// .unwrap();
141 /// assert_eq!(buf, [0x01, 0x02, 0x03]);
142 /// ```
143 pub fn write_items<'a, I>(&mut self, items: I) -> io::Result<()>
144 where
145 I: IntoIterator<Item = &'a Value>,
146 {
147 for item in items {
148 self.write_item(item)?;
149 }
150 Ok(())
151 }
152
153 /// Write each key/value pair as two consecutive items of the
154 /// sequence. Useful for emitting the contents of a map as a flat
155 /// CBOR sequence, mirroring [`Map::from_sequence`](crate::Map::from_sequence)
156 /// on the read side.
157 ///
158 /// The iterator yields borrowed references, which is the natural
159 /// shape of `&BTreeMap<Value, Value>::iter()`, so a map held in a
160 /// `Value` can be streamed directly:
161 ///
162 /// ```
163 /// use cbor_core::{Format, SequenceWriter, Value, map};
164 ///
165 /// let value = map! { "a" => 1, "b" => 2 };
166 /// let mut sw = SequenceWriter::new(Vec::new(), Format::Diagnostic);
167 /// sw.write_pairs(value.as_map().unwrap()).unwrap();
168 /// assert_eq!(sw.into_inner(), br#""a", 1, "b", 2"#);
169 /// ```
170 pub fn write_pairs<'a, I>(&mut self, pairs: I) -> io::Result<()>
171 where
172 I: IntoIterator<Item = (&'a Value, &'a Value)>,
173 {
174 for (k, v) in pairs {
175 self.write_item(k)?;
176 self.write_item(v)?;
177 }
178 Ok(())
179 }
180
181 /// Borrow the wrapped writer.
182 pub const fn get_ref(&self) -> &W {
183 &self.writer
184 }
185
186 /// Mutably borrow the wrapped writer. Direct writes bypass the
187 /// sequence writer's separator bookkeeping, so use this only for
188 /// format-specific ornamentation (for example, injecting a comment
189 /// into diagnostic output) between full items.
190 pub const fn get_mut(&mut self) -> &mut W {
191 &mut self.writer
192 }
193
194 /// Consume the sequence writer and return the wrapped writer.
195 ///
196 /// ```
197 /// use cbor_core::{Format, SequenceWriter, Value};
198 ///
199 /// let mut sw = SequenceWriter::new(Vec::new(), Format::Binary);
200 /// sw.write_item(&Value::from(1)).unwrap();
201 /// sw.write_item(&Value::from(2)).unwrap();
202 /// let buf = sw.into_inner();
203 /// assert_eq!(buf, [0x01, 0x02]);
204 /// ```
205 pub fn into_inner(self) -> W {
206 self.writer
207 }
208}