Skip to main content

deser_csv/
ser.rs

1use alloc::format;
2use alloc::string::String;
3use alloc::string::ToString;
4use alloc::vec;
5use alloc::vec::Vec;
6use core::fmt::{self, Write as _};
7
8use deser_core::__format::{Float, IntBuffer, format_finite};
9use deser_core::ext::Number;
10use deser_core::ser::{self, PausableSink, SerializeDriver, Written};
11use deser_core::{Atom, BytesFormat, Error, ErrorKind, Event, Serialize, State};
12
13use crate::parser::Dialect;
14use crate::{Escape, Nulls, QuoteStyle, Terminator};
15
16/// Configures how values are serialized into delimited text.
17///
18/// The value is a sequence of records.  Records are maps (for instance
19/// structs), the keys of the first record are the names of the columns
20/// which are written first (see [`headers`](Self::headers)), or sequences
21/// (for instance tuples).  Fields are written in the order of the names,
22/// missing fields are empty and keys that are not a column are an error.
23/// Fields cannot hold maps or sequences (see
24/// [`Separated`](deser_core::adapters::Separated) for lists in a field).
25///
26/// Numbers are written with the shortest text that reads back as the same
27/// value, booleans as `true` and `false`, null as an empty field (see
28/// [`nulls`](Self::nulls)) and bytes as base64 (see
29/// [`bytes`](Self::bytes)).  Fields are quoted if necessary (see
30/// [`quote_style`](Self::quote_style)).
31///
32/// ```
33/// use deser_csv::{SerializerConfig, Terminator};
34///
35/// #[derive(deser::Serialize)]
36/// struct Row {
37///     name: &'static str,
38///     note: Option<&'static str>,
39/// }
40///
41/// let rows =
42///     [Row { name: "a", note: Some("x;y") }, Row { name: "b", note: None }];
43/// let config =
44///     SerializerConfig::new().delimiter(b';').terminator(Terminator::CrLf);
45/// assert_eq!(
46///     config.to_string(&rows).unwrap(),
47///     "name;note\r\na;\"x;y\"\r\nb;\r\n"
48/// );
49/// ```
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct SerializerConfig {
52    delimiter: u8,
53    quote: Option<u8>,
54    double_quote: bool,
55    escape: Escape,
56    terminator: Terminator,
57    quote_style: QuoteStyle,
58    headers: bool,
59    columns: Option<&'static [&'static str]>,
60    nulls: Nulls,
61    flexible: bool,
62    escape_formulas: bool,
63    bytes: BytesFormat,
64}
65
66impl Default for SerializerConfig {
67    fn default() -> SerializerConfig {
68        SerializerConfig::new()
69    }
70}
71
72impl SerializerConfig {
73    /// Creates the default configuration (CSV).
74    pub const fn new() -> SerializerConfig {
75        SerializerConfig {
76            delimiter: b',',
77            quote: Some(b'"'),
78            double_quote: true,
79            escape: Escape::None,
80            terminator: Terminator::Newline,
81            quote_style: QuoteStyle::Necessary,
82            headers: true,
83            columns: None,
84            nulls: Nulls::None,
85            flexible: false,
86            escape_formulas: false,
87            bytes: BytesFormat::BASE64,
88        }
89    }
90
91    /// Creates the configuration for tab separated values.
92    ///
93    /// This is the counterpart of
94    /// [`DeserializerConfig::tsv`](crate::DeserializerConfig::tsv): fields
95    /// are separated by tabs, special characters are escaped with
96    /// backslashes and null is `\N`.
97    ///
98    /// ```
99    /// let rows = vec![("a\tb", Some(1)), ("c", None)];
100    /// let tsv = deser_csv::SerializerConfig::tsv().to_string(&rows).unwrap();
101    /// assert_eq!(tsv, "a\\tb\t1\nc\t\\N\n");
102    /// ```
103    pub const fn tsv() -> SerializerConfig {
104        SerializerConfig::new()
105            .delimiter(b'\t')
106            .quote(None)
107            .escape(Escape::Backslash)
108            .nulls(Nulls::Text("\\N"))
109    }
110
111    /// Sets the character that separates fields (`,` by default).
112    pub const fn delimiter(mut self, delimiter: u8) -> SerializerConfig {
113        self.delimiter = delimiter;
114        self
115    }
116
117    /// Sets the character that quotes fields (`"` by default).
118    ///
119    /// Without quotes, fields that need them are an error (unless they
120    /// can be escaped, see [`escape`](Self::escape)).
121    pub const fn quote(mut self, quote: Option<u8>) -> SerializerConfig {
122        self.quote = quote;
123        self
124    }
125
126    /// Sets if quotes in quoted fields are doubled (`true` by default).
127    ///
128    /// Otherwise they are escaped (see [`escape`](Self::escape)).
129    pub const fn double_quote(mut self, yes: bool) -> SerializerConfig {
130        self.double_quote = yes;
131        self
132    }
133
134    /// Sets how characters are escaped (not at all by default).
135    ///
136    /// With an escape character, special characters in unquoted fields are
137    /// escaped instead of quoting the field.
138    pub const fn escape(mut self, escape: Escape) -> SerializerConfig {
139        self.escape = escape;
140        self
141    }
142
143    /// Sets the line ending (`\n` by default, see [`Terminator`]).
144    pub const fn terminator(mut self, terminator: Terminator) -> SerializerConfig {
145        self.terminator = terminator;
146        self
147    }
148
149    /// Sets when fields are quoted ([`QuoteStyle::Necessary`] by default).
150    pub const fn quote_style(mut self, style: QuoteStyle) -> SerializerConfig {
151        self.quote_style = style;
152        self
153    }
154
155    /// Sets if the names of the columns are written before the first
156    /// record (`true` by default).
157    ///
158    /// The names are the keys of the first record (or the given columns,
159    /// see [`columns`](Self::columns)).  Records that are sequences have no
160    /// names.
161    pub const fn headers(mut self, yes: bool) -> SerializerConfig {
162        self.headers = yes;
163        self
164    }
165
166    /// Sets the names of the columns (by default they are the keys of the
167    /// first record).
168    ///
169    /// This is needed if the first record does not have all keys, for
170    /// instance because records are enums or skip fields.  Fields are
171    /// written in the order of the columns, missing fields are empty.
172    ///
173    /// ```
174    /// #[derive(deser::Serialize)]
175    /// #[deser(tag = "kind", rename_all = "lowercase")]
176    /// enum Shape {
177    ///     Circle { radius: f64 },
178    ///     Rect { width: f64, height: f64 },
179    /// }
180    ///
181    /// let shapes = [
182    ///     Shape::Circle { radius: 1.0 },
183    ///     Shape::Rect { width: 2.0, height: 3.0 },
184    /// ];
185    /// let config = deser_csv::SerializerConfig::new()
186    ///     .columns(&["kind", "radius", "width", "height"]);
187    /// assert_eq!(
188    ///     config.to_string(&shapes).unwrap(),
189    ///     "kind,radius,width,height\ncircle,1.0,,\nrect,,2.0,3.0\n"
190    /// );
191    /// ```
192    pub const fn columns(mut self, names: &'static [&'static str]) -> SerializerConfig {
193        self.columns = Some(names);
194        self
195    }
196
197    /// Sets how null is written ([`Nulls::None`] by default).
198    ///
199    /// Null is written as an empty field unless it's [`Nulls::Text`].
200    /// Strings that would read back as null are quoted (the empty string
201    /// with [`Nulls::Empty`]).
202    pub const fn nulls(mut self, nulls: Nulls) -> SerializerConfig {
203        self.nulls = nulls;
204        self
205    }
206
207    /// Sets if records can have a different number of fields (`false` by
208    /// default).
209    pub const fn flexible(mut self, yes: bool) -> SerializerConfig {
210        self.flexible = yes;
211        self
212    }
213
214    /// Sets if strings that spreadsheets would run as formulas are escaped
215    /// (`false` by default).
216    ///
217    /// Spreadsheets run fields that start with `=`, `+`, `-` or `@` (or a
218    /// tab or carriage return) as formulas, which is a problem when a file
219    /// contains data of untrusted users (["CSV
220    /// injection"](https://owasp.org/www-community/attacks/CSV_Injection)).
221    /// With this enabled, such strings are prefixed with `'` and quoted
222    /// (as recommended by OWASP).  Numbers are written as they are.
223    ///
224    /// ```
225    /// let config = deser_csv::SerializerConfig::new().escape_formulas(true);
226    /// let rows = vec![("=1+2", -3)];
227    /// assert_eq!(config.to_string(&rows).unwrap(), "\"'=1+2\",-3\n");
228    /// ```
229    pub const fn escape_formulas(mut self, yes: bool) -> SerializerConfig {
230        self.escape_formulas = yes;
231        self
232    }
233
234    /// Sets how bytes are represented.
235    ///
236    /// By default bytes are written as base64 ([`BytesFormat::BASE64`]).
237    /// Values can request a different format (see
238    /// [bytes](deser_core::adapters#bytes)) which takes precedence.
239    pub const fn bytes(mut self, format: BytesFormat) -> SerializerConfig {
240        self.bytes = format;
241        self
242    }
243
244    /// Serializes the records of a value.
245    ///
246    /// The value has to be a sequence of records.
247    pub fn to_string(&self, value: &dyn Serialize) -> Result<String, Error> {
248        self.to_string_with(value, |_| {})
249    }
250
251    /// Serializes the records of a value with a configured driver.
252    ///
253    /// The callback is invoked with the driver before the serialization
254    /// starts, for instance to add [`Layer`](deser_core::ser::Layer)s.
255    pub fn to_string_with<F>(&self, value: &dyn Serialize, setup: F) -> Result<String, Error>
256    where
257        F: FnOnce(&mut SerializeDriver<'_>),
258    {
259        let mut driver = SerializeDriver::new(value);
260        setup(&mut driver);
261        let mut out = Vec::new();
262        self.write(
263            &mut WriterState::default(),
264            &mut driver,
265            true,
266            &mut out,
267            usize::MAX,
268        )?;
269        Ok(into_string(out))
270    }
271
272    /// Serializes the records (or the record) of a driver and appends them
273    /// to the output.
274    ///
275    /// Only the output is changed if this fails.  Between records, the
276    /// driver is paused once the output holds at least `limit` bytes and
277    /// `false` is returned (the next call continues with the next record).
278    pub(crate) fn write(
279        &self,
280        state: &mut WriterState,
281        driver: &mut SerializeDriver<'_>,
282        document: bool,
283        out: &mut Vec<u8>,
284        limit: usize,
285    ) -> Result<bool, Error> {
286        let dialect = match state.dialect {
287            Some(ref dialect) => dialect,
288            None => state.dialect.insert(Dialect::new(
289                self.delimiter,
290                self.quote,
291                self.double_quote,
292                self.escape,
293                self.terminator,
294                None,
295            )?),
296        };
297        let mut writer = RecordWriter {
298            encoder: FieldEncoder {
299                config: self,
300                dialect,
301            },
302            names: state.names.take(),
303            len: state.len,
304            document,
305            direct: false,
306            is_map: false,
307            fields: 0,
308            record_start: 0,
309            field_ends: core::mem::take(&mut state.buffers.field_ends),
310            record: core::mem::take(&mut state.buffers.record),
311            scratch: core::mem::take(&mut state.buffers.scratch),
312            open: false,
313            limit,
314            out,
315        };
316        let had_names = writer.names.is_some();
317        let rv = if limit == usize::MAX {
318            driver
319                .drive(|event, state| writer.event(event, state))
320                .map(|()| true)
321        } else {
322            driver.drive_until(&mut writer)
323        };
324        // the state only changes if the value was written (or a part of it,
325        // which cannot be taken back)
326        if rv.is_ok() || had_names {
327            state.names = writer.names;
328        }
329        if rv.is_ok() {
330            state.len = writer.len;
331        }
332        // the buffers are reused by the next record
333        state.buffers = Buffers {
334            field_ends: writer.field_ends,
335            record: writer.record,
336            scratch: writer.scratch,
337        };
338        rv
339    }
340}
341
342/// Buffers that are reused for the records of a stream.
343#[derive(Clone, Default)]
344struct Buffers {
345    field_ends: Vec<usize>,
346    record: Record,
347    scratch: Vec<u8>,
348}
349
350impl core::fmt::Debug for Buffers {
351    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352        f.debug_struct("Buffers").finish_non_exhaustive()
353    }
354}
355
356/// The state of a stream of records that is written.
357///
358/// This holds the names of the columns after the first record was written
359/// (or the given names).
360#[derive(Debug, Clone, Default)]
361pub(crate) struct WriterState {
362    names: Option<Vec<String>>,
363    // the number of fields of the records
364    len: Option<usize>,
365    // created with the first record
366    dialect: Option<Dialect>,
367    buffers: Buffers,
368}
369
370impl WriterState {
371    /// Creates the state of a stream that continues with the given names
372    /// of the columns.
373    fn with_headers(names: Vec<String>) -> WriterState {
374        WriterState {
375            len: Some(names.len()),
376            names: Some(names),
377            dialect: None,
378            buffers: Buffers::default(),
379        }
380    }
381}
382
383/// Serializes records into delimited text.
384///
385/// Every value is a record, the names of the columns are written before
386/// the first one.
387///
388/// ```
389/// use deser_csv::Serializer;
390///
391/// #[derive(deser::Serialize)]
392/// struct Row {
393///     name: &'static str,
394///     age: u32,
395/// }
396///
397/// let mut serializer = Serializer::new();
398/// serializer.serialize(&Row { name: "jane", age: 42 }).unwrap();
399/// serializer.serialize(&Row { name: "john", age: 23 }).unwrap();
400/// assert_eq!(serializer.finish(), "name,age\njane,42\njohn,23\n");
401/// ```
402///
403/// The serializer is also the stream serializer of delimited text (see
404/// [`StreamSerializer`](ser::StreamSerializer)): the output can be taken
405/// while records are written.  To write to a [`Write`](std::io::Write) use
406/// [`SerializerConfig::writer`].  A serializer created with
407/// [`document`](Self::document) writes the records of sequences instead,
408/// like [`SerializerConfig::to_string`].
409#[derive(Debug, Clone)]
410pub struct Serializer {
411    config: SerializerConfig,
412    state: WriterState,
413    out: Vec<u8>,
414    // the values are sequences of records
415    document: bool,
416    // a document was started with `drive_partial` and is not complete
417    in_progress: bool,
418}
419
420impl Default for Serializer {
421    fn default() -> Serializer {
422        Serializer::new()
423    }
424}
425
426impl Serializer {
427    /// Creates a serializer.
428    pub fn new() -> Serializer {
429        Serializer::with_config(&SerializerConfig::new())
430    }
431
432    /// Creates a serializer with the given configuration.
433    pub fn with_config(config: &SerializerConfig) -> Serializer {
434        Serializer::with_state(config, WriterState::default(), false)
435    }
436
437    /// Creates a serializer for a stream that continues with the given
438    /// names of the columns.
439    ///
440    /// The names are not written, for instance because the records are
441    /// appended to an existing file.
442    ///
443    /// ```
444    /// use std::collections::BTreeMap;
445    /// use deser_csv::{Serializer, SerializerConfig};
446    ///
447    /// let mut serializer =
448    ///     Serializer::with_headers(&SerializerConfig::new(), ["b", "a"]);
449    /// serializer.serialize(&BTreeMap::from([("a", 1), ("b", 2)])).unwrap();
450    /// assert_eq!(serializer.finish(), "2,1\n");
451    /// ```
452    pub fn with_headers<I, S>(config: &SerializerConfig, names: I) -> Serializer
453    where
454        I: IntoIterator<Item = S>,
455        S: Into<String>,
456    {
457        let names = names.into_iter().map(Into::into).collect();
458        Serializer::with_state(config, WriterState::with_headers(names), false)
459    }
460
461    /// Creates a serializer whose values are sequences of records.
462    ///
463    /// Every value is written like with [`SerializerConfig::to_string`]:
464    /// the elements of the sequence are the records.  The records are
465    /// written while they are serialized, so large documents can be
466    /// written in parts (see
467    /// [`StreamSerializer::drive_partial`](ser::StreamSerializer::drive_partial)).
468    ///
469    /// ```
470    /// use deser_csv::{Serializer, SerializerConfig};
471    ///
472    /// let mut serializer = Serializer::document(&SerializerConfig::new());
473    /// serializer.serialize(&vec![(1, "a"), (2, "b")]).unwrap();
474    /// assert_eq!(serializer.finish(), "1,a\n2,b\n");
475    /// ```
476    pub fn document(config: &SerializerConfig) -> Serializer {
477        Serializer::with_state(config, WriterState::default(), true)
478    }
479
480    fn with_state(config: &SerializerConfig, state: WriterState, document: bool) -> Serializer {
481        Serializer {
482            config: config.clone(),
483            state,
484            out: Vec::new(),
485            document,
486            in_progress: false,
487        }
488    }
489
490    /// Returns the configuration.
491    pub fn config(&self) -> &SerializerConfig {
492        &self.config
493    }
494
495    /// Returns the names of the columns.
496    ///
497    /// This is `None` until the first record was written (unless the names
498    /// were given, see [`with_headers`](Self::with_headers)).
499    pub fn headers(&self) -> Option<&[String]> {
500        self.state.names.as_deref()
501    }
502
503    /// Serializes a record (or the records of a sequence, see
504    /// [`document`](Self::document)).
505    ///
506    /// If the record fails to serialize, nothing is written.
507    pub fn serialize(&mut self, value: &dyn Serialize) -> Result<(), Error> {
508        ser::Serializer::serialize(self, value)
509    }
510
511    /// Serializes a record with a configured driver.
512    ///
513    /// The callback is invoked with the driver before the value is
514    /// serialized, for instance to add [`Layer`](deser_core::ser::Layer)s.
515    pub fn serialize_with<F>(&mut self, value: &dyn Serialize, setup: F) -> Result<(), Error>
516    where
517        F: FnOnce(&mut SerializeDriver<'_>),
518    {
519        ser::Serializer::serialize_with(self, value, setup)
520    }
521
522    /// Returns the output written so far (that was not cleared).
523    pub fn as_str(&self) -> &str {
524        // SAFETY: the output is valid UTF-8, see `into_string`
525        unsafe { core::str::from_utf8_unchecked(&self.out) }
526    }
527
528    /// Returns the output.
529    pub fn finish(self) -> String {
530        into_string(self.out)
531    }
532}
533
534impl ser::Serializer for Serializer {
535    fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
536        if self.in_progress {
537            return Err(Error::in_progress());
538        }
539        let len = self.out.len();
540        match self.config.write(
541            &mut self.state,
542            driver,
543            self.document,
544            &mut self.out,
545            usize::MAX,
546        ) {
547            Ok(_) => Ok(()),
548            Err(err) => {
549                self.out.truncate(len);
550                Err(err)
551            }
552        }
553    }
554}
555
556impl ser::StreamSerializer for Serializer {
557    fn output(&self) -> &[u8] {
558        &self.out
559    }
560
561    fn clear_output(&mut self) {
562        self.out.clear();
563    }
564
565    /// Documents are written in parts (between records).
566    fn supports_partial(&self) -> bool {
567        self.document
568    }
569
570    fn drive_partial(
571        &mut self,
572        driver: &mut SerializeDriver<'_>,
573        limit: usize,
574    ) -> Result<Written, Error> {
575        if !self.document || (limit == usize::MAX && !self.in_progress) {
576            ser::Serializer::drive(self, driver)?;
577            return Ok(Written::Done);
578        }
579        let len = self.out.len();
580        match self
581            .config
582            .write(&mut self.state, driver, true, &mut self.out, limit)
583        {
584            Ok(true) => {
585                self.in_progress = false;
586                Ok(Written::Done)
587            }
588            Ok(false) => {
589                self.in_progress = true;
590                Ok(Written::Partial)
591            }
592            Err(err) => {
593                // the records of the parts that were taken stay written
594                // (and the stream broken, see `in_progress`)
595                self.out.truncate(len);
596                Err(err)
597            }
598        }
599    }
600
601    fn in_progress(&self) -> bool {
602        self.in_progress
603    }
604}
605
606#[cfg(feature = "io")]
607impl SerializerConfig {
608    /// Creates a writer of a stream of records (see
609    /// [`deser::io::Writer`](deser_core::io::Writer)).
610    ///
611    /// Every value is a record, the names of the columns are written before
612    /// the first one (see [`headers`](Self::headers)).  A record that fails
613    /// to serialize is not written.
614    ///
615    /// ```
616    /// use deser_csv::SerializerConfig;
617    ///
618    /// #[derive(deser::Serialize)]
619    /// struct Row {
620    ///     name: &'static str,
621    ///     age: u32,
622    /// }
623    ///
624    /// let mut writer = SerializerConfig::new().writer(Vec::new());
625    /// writer.write(&Row { name: "jane", age: 42 }).unwrap();
626    /// writer.write(&Row { name: "john", age: 23 }).unwrap();
627    /// assert_eq!(writer.into_inner(), b"name,age\njane,42\njohn,23\n");
628    /// ```
629    pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
630        deser_core::io::Writer::new(writer, Serializer::with_config(self))
631    }
632
633    /// Serializes the records of a value to a writer.
634    ///
635    /// See [`to_writer`](crate::to_writer).
636    pub fn to_writer<W: std::io::Write>(
637        &self,
638        writer: W,
639        value: &dyn Serialize,
640    ) -> Result<(), Error> {
641        deser_core::io::to_writer(writer, Serializer::document(self), value)
642    }
643}
644
645/// Serializes the records of a value to a writer.
646///
647/// The records are written while they are serialized (in parts of about
648/// 8 KiB, see [`deser::io`](deser_core::io)), so the writer does not need
649/// to be buffered and the records are not held in memory.  To write one
650/// record at a time use [`SerializerConfig::writer`].
651///
652/// ```
653/// let mut out = Vec::new();
654/// deser_csv::to_writer(&mut out, &vec![(1, "a"), (2, "b")]).unwrap();
655/// assert_eq!(out, b"1,a\n2,b\n");
656/// ```
657#[cfg(feature = "io")]
658pub fn to_writer<W: std::io::Write>(writer: W, value: &dyn Serialize) -> Result<(), Error> {
659    SerializerConfig::new().to_writer(writer, value)
660}
661
662/// Converts the output into a string.
663///
664/// The output only holds text and ASCII special characters (which the
665/// dialect checks), so it's valid UTF-8.
666fn into_string(out: Vec<u8>) -> String {
667    debug_assert!(core::str::from_utf8(&out).is_ok());
668    // SAFETY: see above
669    unsafe { String::from_utf8_unchecked(out) }
670}
671
672/// Serializes the records of a value to delimited text.
673///
674/// This uses the default [`SerializerConfig`] (CSV), see there for more
675/// information.
676///
677/// ```
678/// #[derive(deser::Serialize)]
679/// struct Row {
680///     name: &'static str,
681///     tags: Vec<&'static str>,
682/// }
683///
684/// #[derive(deser::Serialize)]
685/// struct Tagged {
686///     name: &'static str,
687///     #[deser(as = deser::adapters::Separated<';'>)]
688///     tags: Vec<&'static str>,
689/// }
690///
691/// let row = Tagged { name: "a", tags: vec!["x", "y"] };
692/// assert_eq!(deser_csv::to_string(&[row]).unwrap(), "name,tags\na,x;y\n");
693///
694/// let row = Row { name: "a", tags: vec!["x", "y"] };
695/// assert!(deser_csv::to_string(&[row]).is_err());
696/// ```
697pub fn to_string(value: &dyn Serialize) -> Result<String, Error> {
698    SerializerConfig::new().to_string(value)
699}
700
701/// The fields of a record that is collected.
702#[derive(Clone, Default)]
703struct Record {
704    /// The keys (without separators).
705    keys: Vec<u8>,
706    key_ends: Vec<usize>,
707    /// The encoded fields (without separators).
708    fields: Vec<u8>,
709    field_ends: Vec<usize>,
710}
711
712impl Record {
713    fn clear(&mut self) {
714        self.keys.clear();
715        self.key_ends.clear();
716        self.fields.clear();
717        self.field_ends.clear();
718    }
719
720    fn key(&self, index: usize) -> &[u8] {
721        let start = if index == 0 {
722            0
723        } else {
724            self.key_ends[index - 1]
725        };
726        &self.keys[start..self.key_ends[index]]
727    }
728
729    fn field(&self, index: usize) -> &[u8] {
730        let start = if index == 0 {
731            0
732        } else {
733            self.field_ends[index - 1]
734        };
735        &self.fields[start..self.field_ends[index]]
736    }
737}
738
739/// Writes the events of records.
740///
741/// Fields are written directly to the output while the keys of a record
742/// match the names of the columns in their order.  Otherwise (and for the
743/// first record, which comes after the names) the fields are collected and
744/// written in the order of the names at the end of the record.
745struct RecordWriter<'a> {
746    encoder: FieldEncoder<'a>,
747    names: Option<Vec<String>>,
748    len: Option<usize>,
749    document: bool,
750    /// The fields are written to the output directly.
751    direct: bool,
752    is_map: bool,
753    /// The number of fields of the current record.
754    fields: usize,
755    /// Where the current record starts in the output.
756    record_start: usize,
757    /// Where the fields of the current record end in the output (while
758    /// writing directly).
759    field_ends: Vec<usize>,
760    /// The collected record (while not writing directly).
761    record: Record,
762    /// The text of numbers and other atoms that are not text.
763    scratch: Vec<u8>,
764    /// A record is being written.
765    open: bool,
766    /// The driver is paused between records once the output is this long.
767    limit: usize,
768    out: &'a mut Vec<u8>,
769}
770
771impl PausableSink for RecordWriter<'_> {
772    #[inline]
773    fn event(
774        &mut self,
775        event: Event<'_>,
776        _value: &dyn Serialize,
777        state: &mut State,
778    ) -> Result<(), Error> {
779        RecordWriter::event(self, event, state)
780    }
781
782    #[inline]
783    fn pause(&mut self) -> bool {
784        // the fields of a record can still move (see `collect`)
785        !self.open && self.out.len() >= self.limit
786    }
787}
788
789impl RecordWriter<'_> {
790    #[inline]
791    fn event(&mut self, event: Event<'_>, state: &State) -> Result<(), Error> {
792        // most events are the fields of records
793        if let Event::Atom(ref atom) = event
794            && state.depth() == usize::from(self.document) + 1
795        {
796            return self.atom(atom, state.is_map_key());
797        }
798        self.structure(event, state)
799    }
800
801    /// Handles the events that are not fields.
802    fn structure(&mut self, event: Event<'_>, state: &State) -> Result<(), Error> {
803        // the depth of the event, without the container it starts: 0 at the
804        // top level, 1 in a record (2 in the record of a document)
805        let depth = match event {
806            Event::MapStart(_) | Event::SeqStart(_) => state.depth().saturating_sub(1),
807            _ => state.depth(),
808        };
809        let record_depth = usize::from(self.document);
810        match event {
811            Event::SeqStart(_) | Event::SeqEnd if self.document && depth == 0 => {}
812            _ if depth < record_depth => {
813                return Err(Error::new(
814                    ErrorKind::UnsupportedType,
815                    "CSV documents are sequences of records",
816                ));
817            }
818            Event::MapStart(_) | Event::SeqStart(_) if depth == record_depth => {
819                if self.names.is_none()
820                    && let Some(columns) = self.encoder.config.columns
821                {
822                    let names: Vec<String> = columns.iter().map(|name| name.to_string()).collect();
823                    if self.encoder.config.headers {
824                        self.write_names(&names)?;
825                    }
826                    self.names = Some(names);
827                }
828                self.is_map = matches!(event, Event::MapStart(_));
829                self.open = true;
830                self.direct = !self.is_map || self.names.is_some();
831                self.fields = 0;
832                self.record_start = self.out.len();
833                self.field_ends.clear();
834                self.record.clear();
835            }
836            Event::MapEnd | Event::SeqEnd if depth == record_depth => {
837                self.open = false;
838                self.finish_record()?
839            }
840            _ if depth == record_depth => {
841                return Err(Error::new(
842                    ErrorKind::UnsupportedType,
843                    "CSV records must be maps or sequences",
844                ));
845            }
846            // the ends only if layers emitted maps or sequences
847            Event::MapStart(_) | Event::SeqStart(_) | Event::MapEnd | Event::SeqEnd => {
848                return Err(Error::new(
849                    ErrorKind::UnsupportedType,
850                    "CSV fields cannot hold maps or sequences",
851                ));
852            }
853            Event::Atom(ref atom) => return self.atom(atom, state.is_map_key()),
854        }
855        Ok(())
856    }
857
858    /// Handles a key or field of a record.
859    #[inline]
860    fn atom(&mut self, atom: &Atom<'_>, is_key: bool) -> Result<(), Error> {
861        if is_key {
862            return self.key(atom);
863        }
864        let text = self.encoder.text(atom, &mut self.scratch)?;
865        if self.direct {
866            if self.fields > 0 {
867                self.out.push(self.encoder.dialect.delimiter);
868            }
869            self.encoder.encode(text, self.out)?;
870            self.field_ends.push(self.out.len());
871        } else {
872            self.encoder.encode(text, &mut self.record.fields)?;
873            self.record.field_ends.push(self.record.fields.len());
874        }
875        self.fields += 1;
876        Ok(())
877    }
878
879    /// Handles the key of a field.
880    fn key(&mut self, atom: &Atom<'_>) -> Result<(), Error> {
881        let key = match atom {
882            Atom::Null | Atom::Bytes(_) => None,
883            atom => self.encoder.text(atom, &mut self.scratch)?,
884        };
885        let key = key.ok_or_else(unsupported_key)?;
886        if !self.direct {
887            self.record.keys.extend_from_slice(key.bytes);
888        } else {
889            let names = self.names.as_deref().unwrap_or_default();
890            if names.get(self.fields).map(String::as_bytes) == Some(key.bytes) {
891                return Ok(());
892            }
893            let key = key.bytes.to_vec();
894            self.collect();
895            self.record.keys.extend_from_slice(&key);
896        }
897        self.record.key_ends.push(self.record.keys.len());
898        Ok(())
899    }
900
901    /// Moves the fields that were written directly into the record.
902    fn collect(&mut self) {
903        let names = self.names.as_deref().unwrap_or_default();
904        let mut start = self.record_start;
905        for (index, &end) in self.field_ends.iter().enumerate() {
906            if index > 0 {
907                // the delimiter
908                start += 1;
909            }
910            self.record.fields.extend_from_slice(&self.out[start..end]);
911            self.record.field_ends.push(self.record.fields.len());
912            self.record.keys.extend_from_slice(names[index].as_bytes());
913            self.record.key_ends.push(self.record.keys.len());
914            start = end;
915        }
916        self.out.truncate(self.record_start);
917        self.direct = false;
918    }
919
920    /// Ends the record.
921    fn finish_record(&mut self) -> Result<(), Error> {
922        let start = self.record_start;
923        if !self.is_map {
924            self.check_len(self.fields)?;
925            return self.terminate(self.fields, start);
926        }
927
928        if self.direct {
929            // the fields that follow are missing, they are null
930            let len = self.names.as_ref().map_or(0, Vec::len);
931            for column in self.fields..len {
932                if column > 0 {
933                    self.out.push(self.encoder.dialect.delimiter);
934                }
935                self.encoder.encode(None, self.out)?;
936            }
937            self.check_len(len)?;
938            return self.terminate(len, start);
939        }
940
941        let count = self.record.field_ends.len();
942        if self.names.is_none() {
943            // the keys are UTF-8, they are strings or written by us
944            let names: Vec<String> = (0..count)
945                .map(|index| String::from_utf8_lossy(self.record.key(index)).into_owned())
946                .collect();
947            if self.encoder.config.headers {
948                self.write_names(&names)?;
949            }
950            self.names = Some(names);
951        }
952        let len = self.names.as_ref().map_or(0, Vec::len);
953        self.check_len(len)?;
954        let names = self.names.as_ref().unwrap();
955        let start = self.out.len();
956        let mut order = vec![None; len];
957        for index in 0..count {
958            let key = String::from_utf8_lossy(self.record.key(index));
959            match names.iter().position(|name| *name == key) {
960                Some(column) if order[column].is_none() => order[column] = Some(index),
961                Some(_) => {
962                    return Err(Error::new(
963                        ErrorKind::Unexpected,
964                        format!("duplicate field `{}`", key),
965                    ));
966                }
967                None => {
968                    return Err(Error::new(
969                        ErrorKind::Unexpected,
970                        format!("field `{}` is not a column", key),
971                    ));
972                }
973            }
974        }
975        for (column, index) in order.into_iter().enumerate() {
976            if column > 0 {
977                self.out.push(self.encoder.dialect.delimiter);
978            }
979            match index {
980                Some(index) => self.out.extend_from_slice(self.record.field(index)),
981                // missing fields are null
982                None => self.encoder.encode(None, self.out)?,
983            }
984        }
985        self.terminate(len, start)
986    }
987
988    /// Checks the number of fields of a record.
989    fn check_len(&mut self, len: usize) -> Result<(), Error> {
990        match self.len {
991            Some(expected) if expected != len && !self.encoder.config.flexible => Err(Error::new(
992                ErrorKind::Unexpected,
993                format!("record has {} fields, expected {}", len, expected),
994            )),
995            Some(_) => Ok(()),
996            None => {
997                self.len = Some(len);
998                Ok(())
999            }
1000        }
1001    }
1002
1003    /// Ends a record with `len` fields which starts at `start` in the
1004    /// output.
1005    fn terminate(&mut self, len: usize, start: usize) -> Result<(), Error> {
1006        if len == 1 && self.out.len() == start {
1007            // an empty line is a blank line, the only field is quoted
1008            match self.encoder.dialect.quote {
1009                Some(quote) => self.out.extend_from_slice(&[quote, quote]),
1010                None => {
1011                    return Err(Error::new(
1012                        ErrorKind::Unexpected,
1013                        "a record with a single empty field needs quotes",
1014                    ));
1015                }
1016            }
1017        }
1018        match self.encoder.config.terminator {
1019            Terminator::Newline => self.out.push(b'\n'),
1020            Terminator::CrLf => self.out.extend_from_slice(b"\r\n"),
1021            Terminator::Byte(byte) => self.out.push(byte),
1022        }
1023        Ok(())
1024    }
1025
1026    /// Writes the names of the columns.
1027    fn write_names(&mut self, names: &[String]) -> Result<(), Error> {
1028        let start = self.out.len();
1029        for (index, name) in names.iter().enumerate() {
1030            if index > 0 {
1031                self.out.push(self.encoder.dialect.delimiter);
1032            }
1033            let text = Text {
1034                bytes: name.as_bytes(),
1035                numeric: false,
1036            };
1037            self.encoder.encode(Some(text), self.out)?;
1038        }
1039        self.terminate(names.len(), start)
1040    }
1041}
1042
1043/// The text of a field.
1044#[derive(Clone, Copy)]
1045struct Text<'a> {
1046    bytes: &'a [u8],
1047    numeric: bool,
1048}
1049
1050/// Encodes fields.
1051#[derive(Clone, Copy)]
1052struct FieldEncoder<'a> {
1053    config: &'a SerializerConfig,
1054    dialect: &'a Dialect,
1055}
1056
1057impl FieldEncoder<'_> {
1058    /// Encodes a field (`None` for null).
1059    fn encode(&self, text: Option<Text<'_>>, out: &mut Vec<u8>) -> Result<(), Error> {
1060        let config = self.config;
1061        let Some(Text { bytes, numeric }) = text else {
1062            if let Nulls::Text(null) = config.nulls {
1063                out.extend_from_slice(null.as_bytes());
1064            }
1065            return Ok(());
1066        };
1067        let reads_as_null = match config.nulls {
1068            Nulls::None => false,
1069            Nulls::Empty => bytes.is_empty(),
1070            Nulls::Text(null) => bytes == null.as_bytes(),
1071        };
1072        // formulas get a `'` in front and are quoted (as recommended by
1073        // OWASP)
1074        let formula = config.escape_formulas
1075            && !numeric
1076            && matches!(
1077                bytes.first(),
1078                Some(b'=' | b'+' | b'-' | b'@' | b'\t' | b'\r')
1079            );
1080        let has_special = self.dialect.has_special(bytes);
1081        let quote_style = match config.quote_style {
1082            QuoteStyle::Always => true,
1083            QuoteStyle::NonNumeric => !numeric,
1084            QuoteStyle::Necessary | QuoteStyle::Never => false,
1085        };
1086        if !quote_style && !has_special && !reads_as_null && !formula {
1087            out.extend_from_slice(bytes);
1088            return Ok(());
1089        }
1090
1091        let prefix: &[u8] = if formula { b"'" } else { b"" };
1092        let escape = config.escape.byte();
1093        let prefix_is_special = formula && self.dialect.is_special(b'\'');
1094        // without quotes, special characters are escaped (and so is the
1095        // first character of a text that would read as null)
1096        let quoted = quote_style
1097            || (formula && self.dialect.quote.is_some() && config.quote_style != QuoteStyle::Never)
1098            || (escape.is_none() && (has_special || prefix_is_special || reads_as_null))
1099            || (reads_as_null && bytes.is_empty());
1100        let text = prefix.iter().chain(bytes).copied();
1101
1102        if !quoted {
1103            for (index, byte) in text.enumerate() {
1104                if self.dialect.is_special(byte) || (index == 0 && reads_as_null) {
1105                    // `escape` is set, otherwise the field would be quoted
1106                    out.push(escape.unwrap_or(b'\\'));
1107                    out.push(self.escaped(byte));
1108                } else {
1109                    out.push(byte);
1110                }
1111            }
1112            return Ok(());
1113        }
1114
1115        let quote = match self.dialect.quote {
1116            Some(quote) if config.quote_style != QuoteStyle::Never => quote,
1117            _ => {
1118                return Err(Error::new(
1119                    ErrorKind::Unexpected,
1120                    format!(
1121                        "field {:?} needs to be quoted",
1122                        String::from_utf8_lossy(bytes)
1123                    ),
1124                ));
1125            }
1126        };
1127        out.push(quote);
1128        if formula {
1129            self.push_quoted(b'\'', quote, out)?;
1130        }
1131        let mut bytes = bytes;
1132        loop {
1133            // copy the text up to the next character that is escaped
1134            let run = bytes
1135                .iter()
1136                .position(|&b| b == quote || Some(b) == escape)
1137                .unwrap_or(bytes.len());
1138            out.extend_from_slice(&bytes[..run]);
1139            let Some((&byte, rest)) = bytes[run..].split_first() else {
1140                break;
1141            };
1142            bytes = rest;
1143            self.push_quoted(byte, quote, out)?;
1144        }
1145        out.push(quote);
1146        Ok(())
1147    }
1148
1149    /// Writes a character in a quoted field, doubled or escaped if needed.
1150    fn push_quoted(&self, byte: u8, quote: u8, out: &mut Vec<u8>) -> Result<(), Error> {
1151        let escape = self.config.escape.byte();
1152        if byte == quote && self.config.double_quote {
1153            out.extend_from_slice(&[quote, quote]);
1154        } else if byte == quote || Some(byte) == escape {
1155            match escape {
1156                Some(escape) => out.extend_from_slice(&[escape, self.escaped(byte)]),
1157                None => {
1158                    return Err(Error::new(
1159                        ErrorKind::Unexpected,
1160                        "quotes in quoted fields need to be doubled or escaped",
1161                    ));
1162                }
1163            }
1164        } else {
1165            out.push(byte);
1166        }
1167        Ok(())
1168    }
1169
1170    /// Returns what follows the escape character for a byte.
1171    fn escaped(&self, byte: u8) -> u8 {
1172        match (self.config.escape, byte) {
1173            (Escape::Backslash, b'\t') => b't',
1174            (Escape::Backslash, b'\n') => b'n',
1175            (Escape::Backslash, b'\r') => b'r',
1176            (_, byte) => byte,
1177        }
1178    }
1179
1180    /// Returns the text of an atom, `None` for null.
1181    ///
1182    /// Text that is not a string is written into `scratch`.
1183    fn text<'a>(
1184        &self,
1185        atom: &'a Atom<'_>,
1186        scratch: &'a mut Vec<u8>,
1187    ) -> Result<Option<Text<'a>>, Error> {
1188        scratch.clear();
1189        let numeric = match *atom {
1190            Atom::Null => return Ok(None),
1191            Atom::Bool(value) => {
1192                let text: &[u8] = if value { b"true" } else { b"false" };
1193                return Ok(Some(Text {
1194                    bytes: text,
1195                    numeric: false,
1196                }));
1197            }
1198            Atom::Str(ref value) | Atom::Lexical(ref value) => {
1199                return Ok(Some(Text {
1200                    bytes: value.as_bytes(),
1201                    numeric: false,
1202                }));
1203            }
1204            Atom::Char(value) => {
1205                scratch.extend_from_slice(value.encode_utf8(&mut [0; 4]).as_bytes());
1206                false
1207            }
1208            Atom::U64(value) => {
1209                scratch.extend_from_slice(IntBuffer::new().format_u64(value).as_bytes());
1210                true
1211            }
1212            Atom::I64(value) => {
1213                scratch.extend_from_slice(IntBuffer::new().format_i64(value).as_bytes());
1214                true
1215            }
1216            Atom::F32(value) => {
1217                write_float(scratch, value);
1218                true
1219            }
1220            Atom::F64(value) => {
1221                write_float(scratch, value);
1222                true
1223            }
1224            Atom::Bytes(ref bytes) => {
1225                let format = bytes.fallback.copied().unwrap_or(self.config.bytes);
1226                let text = format
1227                    .encode(bytes)
1228                    .or_else(|| BytesFormat::BASE64.encode(bytes))
1229                    .unwrap_or_default();
1230                scratch.extend_from_slice(text.as_bytes());
1231                false
1232            }
1233            Atom::Ext(ref ext) => {
1234                if let Some(number) = ext.downcast_value_ref::<Number>() {
1235                    // numbers keep their text
1236                    scratch.extend_from_slice(number.as_str().as_bytes());
1237                } else if let Some(value) = ext.downcast_ref::<u128>() {
1238                    let _ = write!(ByteWriter(scratch), "{}", value);
1239                } else if let Some(value) = ext.downcast_ref::<i128>() {
1240                    let _ = write!(ByteWriter(scratch), "{}", value);
1241                } else {
1242                    return match ext.fallback() {
1243                        Atom::Ext(_) => Err(Error::new(
1244                            ErrorKind::UnsupportedType,
1245                            format!("CSV does not support {}", ext.name()),
1246                        )),
1247                        fallback => {
1248                            let mut inner = Vec::new();
1249                            let numeric = match self.text(&fallback, &mut inner)? {
1250                                Some(text) => {
1251                                    let numeric = text.numeric;
1252                                    scratch.extend_from_slice(text.bytes);
1253                                    numeric
1254                                }
1255                                None => return Ok(None),
1256                            };
1257                            Ok(Some(Text {
1258                                bytes: scratch,
1259                                numeric,
1260                            }))
1261                        }
1262                    };
1263                }
1264                true
1265            }
1266            // values whose type was inferred from text are written as value
1267            Atom::Implicit(ref value) => {
1268                let mut inner = Vec::new();
1269                return Ok(match self.text(&value.value().to_atom(), &mut inner)? {
1270                    Some(text) => {
1271                        let numeric = text.numeric;
1272                        scratch.extend_from_slice(text.bytes);
1273                        Some(Text {
1274                            bytes: scratch,
1275                            numeric,
1276                        })
1277                    }
1278                    None => None,
1279                });
1280            }
1281            _ => {
1282                return Err(Error::new(
1283                    ErrorKind::UnsupportedType,
1284                    format!("CSV does not support {}", atom.name()),
1285                ));
1286            }
1287        };
1288        Ok(Some(Text {
1289            bytes: scratch,
1290            numeric,
1291        }))
1292    }
1293}
1294
1295/// Formats into a byte buffer (`std::io::Write` is not in `core`).
1296struct ByteWriter<'a>(&'a mut Vec<u8>);
1297
1298impl fmt::Write for ByteWriter<'_> {
1299    fn write_str(&mut self, s: &str) -> fmt::Result {
1300        self.0.extend_from_slice(s.as_bytes());
1301        Ok(())
1302    }
1303}
1304
1305/// Writes a float with the shortest text that reads back as the same value
1306/// of its type (`f32` or `f64`), like the other formats.
1307fn write_float<F: Float>(out: &mut Vec<u8>, value: F) {
1308    if value.is_finite() {
1309        out.extend_from_slice(format_finite(value).as_bytes());
1310    } else {
1311        // `NaN`, `inf` and `-inf`
1312        let _ = write!(ByteWriter(out), "{}", value.to_f64());
1313    }
1314}
1315
1316#[cold]
1317fn unsupported_key() -> Error {
1318    Error::new(
1319        ErrorKind::UnsupportedType,
1320        "the keys of records must be strings, numbers or booleans",
1321    )
1322}