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_whole(&mut WriterState::default(), &mut driver, true, &mut out)?;
263        Ok(into_string(out))
264    }
265
266    /// Serializes the records (or the record) of a driver and appends them
267    /// to the output.
268    ///
269    /// Only the output is changed if this fails.  Between records, the
270    /// driver is paused once the output holds at least `limit` bytes and
271    /// `false` is returned (the next call continues with the next record).
272    pub(crate) fn write(
273        &self,
274        state: &mut WriterState,
275        driver: &mut SerializeDriver<'_>,
276        document: bool,
277        out: &mut Vec<u8>,
278        limit: usize,
279    ) -> Result<bool, Error> {
280        let drive: DriveFn = if limit == usize::MAX {
281            drive_whole
282        } else {
283            drive_partial
284        };
285        self.write_with(state, driver, document, out, limit, drive)
286    }
287
288    /// Serializes the records of a driver at once and appends them to the
289    /// output (see `write`).
290    ///
291    /// Unlike `write` this does not refer to the pausable instance of the
292    /// driver which is only needed by stream serializers.
293    pub(crate) fn write_whole(
294        &self,
295        state: &mut WriterState,
296        driver: &mut SerializeDriver<'_>,
297        document: bool,
298        out: &mut Vec<u8>,
299    ) -> Result<(), Error> {
300        self.write_with(state, driver, document, out, usize::MAX, drive_whole)
301            .map(|_| ())
302    }
303
304    /// Implements `write` with the function that drives the driver.
305    fn write_with(
306        &self,
307        state: &mut WriterState,
308        driver: &mut SerializeDriver<'_>,
309        document: bool,
310        out: &mut Vec<u8>,
311        limit: usize,
312        drive: DriveFn,
313    ) -> Result<bool, Error> {
314        let dialect = match state.dialect {
315            Some(ref dialect) => dialect,
316            None => state.dialect.insert(Dialect::new(
317                self.delimiter,
318                self.quote,
319                self.double_quote,
320                self.escape,
321                self.terminator,
322                None,
323            )?),
324        };
325        let mut writer = RecordWriter {
326            encoder: FieldEncoder {
327                config: self,
328                dialect,
329            },
330            names: state.names.take(),
331            len: state.len,
332            document,
333            direct: false,
334            is_map: false,
335            fields: 0,
336            record_start: 0,
337            field_ends: core::mem::take(&mut state.buffers.field_ends),
338            record: core::mem::take(&mut state.buffers.record),
339            scratch: core::mem::take(&mut state.buffers.scratch),
340            open: false,
341            limit,
342            out,
343        };
344        let had_names = writer.names.is_some();
345        let rv = drive(driver, &mut writer);
346        // the state only changes if the value was written (or a part of it,
347        // which cannot be taken back)
348        if rv.is_ok() || had_names {
349            state.names = writer.names;
350        }
351        if rv.is_ok() {
352            state.len = writer.len;
353        }
354        // the buffers are reused by the next record
355        state.buffers = Buffers {
356            field_ends: writer.field_ends,
357            record: writer.record,
358            scratch: writer.scratch,
359        };
360        rv
361    }
362}
363
364/// Drives a driver into a record writer (see `SerializerConfig::write_with`).
365type DriveFn = fn(&mut SerializeDriver<'_>, &mut RecordWriter<'_>) -> Result<bool, Error>;
366
367/// Writes the records of a driver at once.
368fn drive_whole(
369    driver: &mut SerializeDriver<'_>,
370    writer: &mut RecordWriter<'_>,
371) -> Result<bool, Error> {
372    driver
373        .drive(|event, state| writer.event(event, state))
374        .map(|()| true)
375}
376
377/// Writes the records of a driver until the writer pauses it.
378fn drive_partial(
379    driver: &mut SerializeDriver<'_>,
380    writer: &mut RecordWriter<'_>,
381) -> Result<bool, Error> {
382    driver.drive_until(writer)
383}
384
385/// Buffers that are reused for the records of a stream.
386#[derive(Clone, Default)]
387struct Buffers {
388    field_ends: Vec<usize>,
389    record: Record,
390    scratch: Vec<u8>,
391}
392
393impl core::fmt::Debug for Buffers {
394    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
395        f.debug_struct("Buffers").finish_non_exhaustive()
396    }
397}
398
399/// The state of a stream of records that is written.
400///
401/// This holds the names of the columns after the first record was written
402/// (or the given names).
403#[derive(Debug, Clone, Default)]
404pub(crate) struct WriterState {
405    names: Option<Vec<String>>,
406    // the number of fields of the records
407    len: Option<usize>,
408    // created with the first record
409    dialect: Option<Dialect>,
410    buffers: Buffers,
411}
412
413impl WriterState {
414    /// Creates the state of a stream that continues with the given names
415    /// of the columns.
416    fn with_headers(names: Vec<String>) -> WriterState {
417        WriterState {
418            len: Some(names.len()),
419            names: Some(names),
420            dialect: None,
421            buffers: Buffers::default(),
422        }
423    }
424}
425
426/// Serializes records into delimited text.
427///
428/// Every value is a record, the names of the columns are written before
429/// the first one.
430///
431/// ```
432/// use deser_csv::Serializer;
433///
434/// #[derive(deser::Serialize)]
435/// struct Row {
436///     name: &'static str,
437///     age: u32,
438/// }
439///
440/// let mut serializer = Serializer::new();
441/// serializer.serialize(&Row { name: "jane", age: 42 }).unwrap();
442/// serializer.serialize(&Row { name: "john", age: 23 }).unwrap();
443/// assert_eq!(serializer.finish(), "name,age\njane,42\njohn,23\n");
444/// ```
445///
446/// The serializer is also the stream serializer of delimited text (see
447/// [`StreamSerializer`](ser::StreamSerializer)): the output can be taken
448/// while records are written.  To write to a [`Write`](std::io::Write) use
449/// [`SerializerConfig::writer`].  A serializer created with
450/// [`document`](Self::document) writes the records of sequences instead,
451/// like [`SerializerConfig::to_string`].
452#[derive(Debug, Clone)]
453pub struct Serializer {
454    config: SerializerConfig,
455    state: WriterState,
456    out: Vec<u8>,
457    // the values are sequences of records
458    document: bool,
459    // a document was started with `drive_partial` and is not complete
460    in_progress: bool,
461}
462
463impl Default for Serializer {
464    fn default() -> Serializer {
465        Serializer::new()
466    }
467}
468
469impl Serializer {
470    /// Creates a serializer.
471    pub fn new() -> Serializer {
472        Serializer::with_config(&SerializerConfig::new())
473    }
474
475    /// Creates a serializer with the given configuration.
476    pub fn with_config(config: &SerializerConfig) -> Serializer {
477        Serializer::with_state(config, WriterState::default(), false)
478    }
479
480    /// Creates a serializer for a stream that continues with the given
481    /// names of the columns.
482    ///
483    /// The names are not written, for instance because the records are
484    /// appended to an existing file.
485    ///
486    /// ```
487    /// use std::collections::BTreeMap;
488    /// use deser_csv::{Serializer, SerializerConfig};
489    ///
490    /// let mut serializer =
491    ///     Serializer::with_headers(&SerializerConfig::new(), ["b", "a"]);
492    /// serializer.serialize(&BTreeMap::from([("a", 1), ("b", 2)])).unwrap();
493    /// assert_eq!(serializer.finish(), "2,1\n");
494    /// ```
495    pub fn with_headers<I, S>(config: &SerializerConfig, names: I) -> Serializer
496    where
497        I: IntoIterator<Item = S>,
498        S: Into<String>,
499    {
500        let names = names.into_iter().map(Into::into).collect();
501        Serializer::with_state(config, WriterState::with_headers(names), false)
502    }
503
504    /// Creates a serializer whose values are sequences of records.
505    ///
506    /// Every value is written like with [`SerializerConfig::to_string`]:
507    /// the elements of the sequence are the records.  The records are
508    /// written while they are serialized, so large documents can be
509    /// written in parts (see
510    /// [`StreamSerializer::drive_partial`](ser::StreamSerializer::drive_partial)).
511    ///
512    /// ```
513    /// use deser_csv::{Serializer, SerializerConfig};
514    ///
515    /// let mut serializer = Serializer::document(&SerializerConfig::new());
516    /// serializer.serialize(&vec![(1, "a"), (2, "b")]).unwrap();
517    /// assert_eq!(serializer.finish(), "1,a\n2,b\n");
518    /// ```
519    pub fn document(config: &SerializerConfig) -> Serializer {
520        Serializer::with_state(config, WriterState::default(), true)
521    }
522
523    fn with_state(config: &SerializerConfig, state: WriterState, document: bool) -> Serializer {
524        Serializer {
525            config: config.clone(),
526            state,
527            out: Vec::new(),
528            document,
529            in_progress: false,
530        }
531    }
532
533    /// Returns the configuration.
534    pub fn config(&self) -> &SerializerConfig {
535        &self.config
536    }
537
538    /// Returns the names of the columns.
539    ///
540    /// This is `None` until the first record was written (unless the names
541    /// were given, see [`with_headers`](Self::with_headers)).
542    pub fn headers(&self) -> Option<&[String]> {
543        self.state.names.as_deref()
544    }
545
546    /// Serializes a record (or the records of a sequence, see
547    /// [`document`](Self::document)).
548    ///
549    /// If the record fails to serialize, nothing is written.
550    pub fn serialize(&mut self, value: &dyn Serialize) -> Result<(), Error> {
551        ser::Serializer::serialize(self, value)
552    }
553
554    /// Serializes a record with a configured driver.
555    ///
556    /// The callback is invoked with the driver before the value is
557    /// serialized, for instance to add [`Layer`](deser_core::ser::Layer)s.
558    pub fn serialize_with<F>(&mut self, value: &dyn Serialize, setup: F) -> Result<(), Error>
559    where
560        F: FnOnce(&mut SerializeDriver<'_>),
561    {
562        ser::Serializer::serialize_with(self, value, setup)
563    }
564
565    /// Returns the output written so far (that was not cleared).
566    pub fn as_str(&self) -> &str {
567        // SAFETY: the output is valid UTF-8, see `into_string`
568        unsafe { core::str::from_utf8_unchecked(&self.out) }
569    }
570
571    /// Returns the output.
572    pub fn finish(self) -> String {
573        into_string(self.out)
574    }
575}
576
577impl ser::Serializer for Serializer {
578    fn drive(&mut self, driver: &mut SerializeDriver<'_>) -> Result<(), Error> {
579        if self.in_progress {
580            return Err(Error::in_progress());
581        }
582        let len = self.out.len();
583        match self
584            .config
585            .write_whole(&mut self.state, driver, self.document, &mut self.out)
586        {
587            Ok(()) => Ok(()),
588            Err(err) => {
589                self.out.truncate(len);
590                Err(err)
591            }
592        }
593    }
594}
595
596impl ser::StreamSerializer for Serializer {
597    fn output(&self) -> &[u8] {
598        &self.out
599    }
600
601    fn clear_output(&mut self) {
602        self.out.clear();
603    }
604
605    /// Documents are written in parts (between records).
606    fn supports_partial(&self) -> bool {
607        self.document
608    }
609
610    fn drive_partial(
611        &mut self,
612        driver: &mut SerializeDriver<'_>,
613        limit: usize,
614    ) -> Result<Written, Error> {
615        if !self.document || (limit == usize::MAX && !self.in_progress) {
616            ser::Serializer::drive(self, driver)?;
617            return Ok(Written::Done);
618        }
619        let len = self.out.len();
620        match self
621            .config
622            .write(&mut self.state, driver, true, &mut self.out, limit)
623        {
624            Ok(true) => {
625                self.in_progress = false;
626                Ok(Written::Done)
627            }
628            Ok(false) => {
629                self.in_progress = true;
630                Ok(Written::Partial)
631            }
632            Err(err) => {
633                // the records of the parts that were taken stay written
634                // (and the stream broken, see `in_progress`)
635                self.out.truncate(len);
636                Err(err)
637            }
638        }
639    }
640
641    fn in_progress(&self) -> bool {
642        self.in_progress
643    }
644}
645
646#[cfg(feature = "io")]
647impl SerializerConfig {
648    /// Creates a writer of a stream of records (see
649    /// [`deser::io::Writer`](deser_core::io::Writer)).
650    ///
651    /// Every value is a record, the names of the columns are written before
652    /// the first one (see [`headers`](Self::headers)).  A record that fails
653    /// to serialize is not written.
654    ///
655    /// ```
656    /// use deser_csv::SerializerConfig;
657    ///
658    /// #[derive(deser::Serialize)]
659    /// struct Row {
660    ///     name: &'static str,
661    ///     age: u32,
662    /// }
663    ///
664    /// let mut writer = SerializerConfig::new().writer(Vec::new());
665    /// writer.write(&Row { name: "jane", age: 42 }).unwrap();
666    /// writer.write(&Row { name: "john", age: 23 }).unwrap();
667    /// assert_eq!(writer.into_inner(), b"name,age\njane,42\njohn,23\n");
668    /// ```
669    pub fn writer<W: std::io::Write>(&self, writer: W) -> deser_core::io::Writer<W, Serializer> {
670        deser_core::io::Writer::new(writer, Serializer::with_config(self))
671    }
672
673    /// Serializes the records of a value to a writer.
674    ///
675    /// See [`to_writer`](crate::to_writer).
676    pub fn to_writer<W: std::io::Write>(
677        &self,
678        writer: W,
679        value: &dyn Serialize,
680    ) -> Result<(), Error> {
681        deser_core::io::to_writer(writer, Serializer::document(self), value)
682    }
683}
684
685/// Serializes the records of a value to a writer.
686///
687/// The records are written while they are serialized (in parts of about
688/// 8 KiB, see [`deser::io`](deser_core::io)), so the writer does not need
689/// to be buffered and the records are not held in memory.  To write one
690/// record at a time use [`SerializerConfig::writer`].
691///
692/// ```
693/// let mut out = Vec::new();
694/// deser_csv::to_writer(&mut out, &vec![(1, "a"), (2, "b")]).unwrap();
695/// assert_eq!(out, b"1,a\n2,b\n");
696/// ```
697#[cfg(feature = "io")]
698pub fn to_writer<W: std::io::Write>(writer: W, value: &dyn Serialize) -> Result<(), Error> {
699    SerializerConfig::new().to_writer(writer, value)
700}
701
702/// Converts the output into a string.
703///
704/// The output only holds text and ASCII special characters (which the
705/// dialect checks), so it's valid UTF-8.
706fn into_string(out: Vec<u8>) -> String {
707    debug_assert!(core::str::from_utf8(&out).is_ok());
708    // SAFETY: see above
709    unsafe { String::from_utf8_unchecked(out) }
710}
711
712/// Serializes the records of a value to delimited text.
713///
714/// This uses the default [`SerializerConfig`] (CSV), see there for more
715/// information.
716///
717/// ```
718/// #[derive(deser::Serialize)]
719/// struct Row {
720///     name: &'static str,
721///     tags: Vec<&'static str>,
722/// }
723///
724/// #[derive(deser::Serialize)]
725/// struct Tagged {
726///     name: &'static str,
727///     #[deser(as = deser::adapters::Separated<';'>)]
728///     tags: Vec<&'static str>,
729/// }
730///
731/// let row = Tagged { name: "a", tags: vec!["x", "y"] };
732/// assert_eq!(deser_csv::to_string(&[row]).unwrap(), "name,tags\na,x;y\n");
733///
734/// let row = Row { name: "a", tags: vec!["x", "y"] };
735/// assert!(deser_csv::to_string(&[row]).is_err());
736/// ```
737pub fn to_string(value: &dyn Serialize) -> Result<String, Error> {
738    SerializerConfig::new().to_string(value)
739}
740
741/// The fields of a record that is collected.
742#[derive(Clone, Default)]
743struct Record {
744    /// The keys (without separators).
745    keys: Vec<u8>,
746    key_ends: Vec<usize>,
747    /// The encoded fields (without separators).
748    fields: Vec<u8>,
749    field_ends: Vec<usize>,
750}
751
752impl Record {
753    fn clear(&mut self) {
754        self.keys.clear();
755        self.key_ends.clear();
756        self.fields.clear();
757        self.field_ends.clear();
758    }
759
760    fn key(&self, index: usize) -> &[u8] {
761        let start = if index == 0 {
762            0
763        } else {
764            self.key_ends[index - 1]
765        };
766        &self.keys[start..self.key_ends[index]]
767    }
768
769    fn field(&self, index: usize) -> &[u8] {
770        let start = if index == 0 {
771            0
772        } else {
773            self.field_ends[index - 1]
774        };
775        &self.fields[start..self.field_ends[index]]
776    }
777}
778
779/// Writes the events of records.
780///
781/// Fields are written directly to the output while the keys of a record
782/// match the names of the columns in their order.  Otherwise (and for the
783/// first record, which comes after the names) the fields are collected and
784/// written in the order of the names at the end of the record.
785struct RecordWriter<'a> {
786    encoder: FieldEncoder<'a>,
787    names: Option<Vec<String>>,
788    len: Option<usize>,
789    document: bool,
790    /// The fields are written to the output directly.
791    direct: bool,
792    is_map: bool,
793    /// The number of fields of the current record.
794    fields: usize,
795    /// Where the current record starts in the output.
796    record_start: usize,
797    /// Where the fields of the current record end in the output (while
798    /// writing directly).
799    field_ends: Vec<usize>,
800    /// The collected record (while not writing directly).
801    record: Record,
802    /// The text of numbers and other atoms that are not text.
803    scratch: Vec<u8>,
804    /// A record is being written.
805    open: bool,
806    /// The driver is paused between records once the output is this long.
807    limit: usize,
808    out: &'a mut Vec<u8>,
809}
810
811impl PausableSink for RecordWriter<'_> {
812    #[inline]
813    fn event(
814        &mut self,
815        event: Event<'_>,
816        _value: &dyn Serialize,
817        state: &mut State,
818    ) -> Result<(), Error> {
819        RecordWriter::event(self, event, state)
820    }
821
822    #[inline]
823    fn pause(&mut self) -> bool {
824        // the fields of a record can still move (see `collect`)
825        !self.open && self.out.len() >= self.limit
826    }
827}
828
829impl RecordWriter<'_> {
830    #[inline]
831    fn event(&mut self, event: Event<'_>, state: &State) -> Result<(), Error> {
832        // most events are the fields of records
833        if let Event::Atom(ref atom) = event
834            && state.depth() == usize::from(self.document) + 1
835        {
836            return self.atom(atom, state.is_map_key());
837        }
838        self.structure(event, state)
839    }
840
841    /// Handles the events that are not fields.
842    fn structure(&mut self, event: Event<'_>, state: &State) -> Result<(), Error> {
843        // the depth of the event, without the container it starts: 0 at the
844        // top level, 1 in a record (2 in the record of a document)
845        let depth = match event {
846            Event::MapStart(_) | Event::SeqStart(_) => state.depth().saturating_sub(1),
847            _ => state.depth(),
848        };
849        let record_depth = usize::from(self.document);
850        match event {
851            Event::SeqStart(_) | Event::SeqEnd if self.document && depth == 0 => {}
852            _ if depth < record_depth => {
853                return Err(Error::new(
854                    ErrorKind::UnsupportedType,
855                    "CSV documents are sequences of records",
856                ));
857            }
858            Event::MapStart(_) | Event::SeqStart(_) if depth == record_depth => {
859                if self.names.is_none()
860                    && let Some(columns) = self.encoder.config.columns
861                {
862                    let names: Vec<String> = columns.iter().map(|name| name.to_string()).collect();
863                    if self.encoder.config.headers {
864                        self.write_names(&names)?;
865                    }
866                    self.names = Some(names);
867                }
868                self.is_map = matches!(event, Event::MapStart(_));
869                self.open = true;
870                self.direct = !self.is_map || self.names.is_some();
871                self.fields = 0;
872                self.record_start = self.out.len();
873                self.field_ends.clear();
874                self.record.clear();
875            }
876            Event::MapEnd | Event::SeqEnd if depth == record_depth => {
877                self.open = false;
878                self.finish_record()?
879            }
880            _ if depth == record_depth => {
881                return Err(Error::new(
882                    ErrorKind::UnsupportedType,
883                    "CSV records must be maps or sequences",
884                ));
885            }
886            // the ends only if layers emitted maps or sequences
887            Event::MapStart(_) | Event::SeqStart(_) | Event::MapEnd | Event::SeqEnd => {
888                return Err(Error::new(
889                    ErrorKind::UnsupportedType,
890                    "CSV fields cannot hold maps or sequences",
891                ));
892            }
893            Event::Atom(ref atom) => return self.atom(atom, state.is_map_key()),
894        }
895        Ok(())
896    }
897
898    /// Handles a key or field of a record.
899    #[inline]
900    fn atom(&mut self, atom: &Atom<'_>, is_key: bool) -> Result<(), Error> {
901        if is_key {
902            return self.key(atom);
903        }
904        let text = self.encoder.text(atom, &mut self.scratch)?;
905        if self.direct {
906            if self.fields > 0 {
907                self.out.push(self.encoder.dialect.delimiter);
908            }
909            self.encoder.encode(text, self.out)?;
910            self.field_ends.push(self.out.len());
911        } else {
912            self.encoder.encode(text, &mut self.record.fields)?;
913            self.record.field_ends.push(self.record.fields.len());
914        }
915        self.fields += 1;
916        Ok(())
917    }
918
919    /// Handles the key of a field.
920    fn key(&mut self, atom: &Atom<'_>) -> Result<(), Error> {
921        let key = match atom {
922            Atom::Null | Atom::Bytes(_) => None,
923            atom => self.encoder.text(atom, &mut self.scratch)?,
924        };
925        let key = key.ok_or_else(unsupported_key)?;
926        if !self.direct {
927            self.record.keys.extend_from_slice(key.bytes);
928        } else {
929            let names = self.names.as_deref().unwrap_or_default();
930            if names.get(self.fields).map(String::as_bytes) == Some(key.bytes) {
931                return Ok(());
932            }
933            let key = key.bytes.to_vec();
934            self.collect();
935            self.record.keys.extend_from_slice(&key);
936        }
937        self.record.key_ends.push(self.record.keys.len());
938        Ok(())
939    }
940
941    /// Moves the fields that were written directly into the record.
942    fn collect(&mut self) {
943        let names = self.names.as_deref().unwrap_or_default();
944        let mut start = self.record_start;
945        for (index, &end) in self.field_ends.iter().enumerate() {
946            if index > 0 {
947                // the delimiter
948                start += 1;
949            }
950            self.record.fields.extend_from_slice(&self.out[start..end]);
951            self.record.field_ends.push(self.record.fields.len());
952            self.record.keys.extend_from_slice(names[index].as_bytes());
953            self.record.key_ends.push(self.record.keys.len());
954            start = end;
955        }
956        self.out.truncate(self.record_start);
957        self.direct = false;
958    }
959
960    /// Ends the record.
961    fn finish_record(&mut self) -> Result<(), Error> {
962        let start = self.record_start;
963        if !self.is_map {
964            self.check_len(self.fields)?;
965            return self.terminate(self.fields, start);
966        }
967
968        if self.direct {
969            // the fields that follow are missing, they are null
970            let len = self.names.as_ref().map_or(0, Vec::len);
971            for column in self.fields..len {
972                if column > 0 {
973                    self.out.push(self.encoder.dialect.delimiter);
974                }
975                self.encoder.encode(None, self.out)?;
976            }
977            self.check_len(len)?;
978            return self.terminate(len, start);
979        }
980
981        let count = self.record.field_ends.len();
982        if self.names.is_none() {
983            // the keys are UTF-8, they are strings or written by us
984            let names: Vec<String> = (0..count)
985                .map(|index| String::from_utf8_lossy(self.record.key(index)).into_owned())
986                .collect();
987            if self.encoder.config.headers {
988                self.write_names(&names)?;
989            }
990            self.names = Some(names);
991        }
992        let len = self.names.as_ref().map_or(0, Vec::len);
993        self.check_len(len)?;
994        let names = self.names.as_ref().unwrap();
995        let start = self.out.len();
996        let mut order = vec![None; len];
997        for index in 0..count {
998            let key = String::from_utf8_lossy(self.record.key(index));
999            match names.iter().position(|name| *name == key) {
1000                Some(column) if order[column].is_none() => order[column] = Some(index),
1001                Some(_) => {
1002                    return Err(Error::new(
1003                        ErrorKind::Unexpected,
1004                        format!("duplicate field `{}`", key),
1005                    ));
1006                }
1007                None => {
1008                    return Err(Error::new(
1009                        ErrorKind::Unexpected,
1010                        format!("field `{}` is not a column", key),
1011                    ));
1012                }
1013            }
1014        }
1015        for (column, index) in order.into_iter().enumerate() {
1016            if column > 0 {
1017                self.out.push(self.encoder.dialect.delimiter);
1018            }
1019            match index {
1020                Some(index) => self.out.extend_from_slice(self.record.field(index)),
1021                // missing fields are null
1022                None => self.encoder.encode(None, self.out)?,
1023            }
1024        }
1025        self.terminate(len, start)
1026    }
1027
1028    /// Checks the number of fields of a record.
1029    fn check_len(&mut self, len: usize) -> Result<(), Error> {
1030        match self.len {
1031            Some(expected) if expected != len && !self.encoder.config.flexible => Err(Error::new(
1032                ErrorKind::Unexpected,
1033                format!("record has {} fields, expected {}", len, expected),
1034            )),
1035            Some(_) => Ok(()),
1036            None => {
1037                self.len = Some(len);
1038                Ok(())
1039            }
1040        }
1041    }
1042
1043    /// Ends a record with `len` fields which starts at `start` in the
1044    /// output.
1045    fn terminate(&mut self, len: usize, start: usize) -> Result<(), Error> {
1046        if len == 1 && self.out.len() == start {
1047            // an empty line is a blank line, the only field is quoted
1048            match self.encoder.dialect.quote {
1049                Some(quote) => self.out.extend_from_slice(&[quote, quote]),
1050                None => {
1051                    return Err(Error::new(
1052                        ErrorKind::Unexpected,
1053                        "a record with a single empty field needs quotes",
1054                    ));
1055                }
1056            }
1057        }
1058        match self.encoder.config.terminator {
1059            Terminator::Newline => self.out.push(b'\n'),
1060            Terminator::CrLf => self.out.extend_from_slice(b"\r\n"),
1061            Terminator::Byte(byte) => self.out.push(byte),
1062        }
1063        Ok(())
1064    }
1065
1066    /// Writes the names of the columns.
1067    fn write_names(&mut self, names: &[String]) -> Result<(), Error> {
1068        let start = self.out.len();
1069        for (index, name) in names.iter().enumerate() {
1070            if index > 0 {
1071                self.out.push(self.encoder.dialect.delimiter);
1072            }
1073            let text = Text {
1074                bytes: name.as_bytes(),
1075                numeric: false,
1076            };
1077            self.encoder.encode(Some(text), self.out)?;
1078        }
1079        self.terminate(names.len(), start)
1080    }
1081}
1082
1083/// The text of a field.
1084#[derive(Clone, Copy)]
1085struct Text<'a> {
1086    bytes: &'a [u8],
1087    numeric: bool,
1088}
1089
1090/// Encodes fields.
1091#[derive(Clone, Copy)]
1092struct FieldEncoder<'a> {
1093    config: &'a SerializerConfig,
1094    dialect: &'a Dialect,
1095}
1096
1097impl FieldEncoder<'_> {
1098    /// Encodes a field (`None` for null).
1099    fn encode(&self, text: Option<Text<'_>>, out: &mut Vec<u8>) -> Result<(), Error> {
1100        let config = self.config;
1101        let Some(Text { bytes, numeric }) = text else {
1102            if let Nulls::Text(null) = config.nulls {
1103                out.extend_from_slice(null.as_bytes());
1104            }
1105            return Ok(());
1106        };
1107        let reads_as_null = match config.nulls {
1108            Nulls::None => false,
1109            Nulls::Empty => bytes.is_empty(),
1110            Nulls::Text(null) => bytes == null.as_bytes(),
1111        };
1112        // formulas get a `'` in front and are quoted (as recommended by
1113        // OWASP)
1114        let formula = config.escape_formulas
1115            && !numeric
1116            && matches!(
1117                bytes.first(),
1118                Some(b'=' | b'+' | b'-' | b'@' | b'\t' | b'\r')
1119            );
1120        let has_special = self.dialect.has_special(bytes);
1121        let quote_style = match config.quote_style {
1122            QuoteStyle::Always => true,
1123            QuoteStyle::NonNumeric => !numeric,
1124            QuoteStyle::Necessary | QuoteStyle::Never => false,
1125        };
1126        if !quote_style && !has_special && !reads_as_null && !formula {
1127            out.extend_from_slice(bytes);
1128            return Ok(());
1129        }
1130
1131        let prefix: &[u8] = if formula { b"'" } else { b"" };
1132        let escape = config.escape.byte();
1133        let prefix_is_special = formula && self.dialect.is_special(b'\'');
1134        // without quotes, special characters are escaped (and so is the
1135        // first character of a text that would read as null)
1136        let quoted = quote_style
1137            || (formula && self.dialect.quote.is_some() && config.quote_style != QuoteStyle::Never)
1138            || (escape.is_none() && (has_special || prefix_is_special || reads_as_null))
1139            || (reads_as_null && bytes.is_empty());
1140        let text = prefix.iter().chain(bytes).copied();
1141
1142        if !quoted {
1143            for (index, byte) in text.enumerate() {
1144                if self.dialect.is_special(byte) || (index == 0 && reads_as_null) {
1145                    // `escape` is set, otherwise the field would be quoted
1146                    out.push(escape.unwrap_or(b'\\'));
1147                    out.push(self.escaped(byte));
1148                } else {
1149                    out.push(byte);
1150                }
1151            }
1152            return Ok(());
1153        }
1154
1155        let quote = match self.dialect.quote {
1156            Some(quote) if config.quote_style != QuoteStyle::Never => quote,
1157            _ => {
1158                return Err(Error::new(
1159                    ErrorKind::Unexpected,
1160                    format!(
1161                        "field {:?} needs to be quoted",
1162                        String::from_utf8_lossy(bytes)
1163                    ),
1164                ));
1165            }
1166        };
1167        out.push(quote);
1168        if formula {
1169            self.push_quoted(b'\'', quote, out)?;
1170        }
1171        let mut bytes = bytes;
1172        loop {
1173            // copy the text up to the next character that is escaped
1174            let run = bytes
1175                .iter()
1176                .position(|&b| b == quote || Some(b) == escape)
1177                .unwrap_or(bytes.len());
1178            out.extend_from_slice(&bytes[..run]);
1179            let Some((&byte, rest)) = bytes[run..].split_first() else {
1180                break;
1181            };
1182            bytes = rest;
1183            self.push_quoted(byte, quote, out)?;
1184        }
1185        out.push(quote);
1186        Ok(())
1187    }
1188
1189    /// Writes a character in a quoted field, doubled or escaped if needed.
1190    fn push_quoted(&self, byte: u8, quote: u8, out: &mut Vec<u8>) -> Result<(), Error> {
1191        let escape = self.config.escape.byte();
1192        if byte == quote && self.config.double_quote {
1193            out.extend_from_slice(&[quote, quote]);
1194        } else if byte == quote || Some(byte) == escape {
1195            match escape {
1196                Some(escape) => out.extend_from_slice(&[escape, self.escaped(byte)]),
1197                None => {
1198                    return Err(Error::new(
1199                        ErrorKind::Unexpected,
1200                        "quotes in quoted fields need to be doubled or escaped",
1201                    ));
1202                }
1203            }
1204        } else {
1205            out.push(byte);
1206        }
1207        Ok(())
1208    }
1209
1210    /// Returns what follows the escape character for a byte.
1211    fn escaped(&self, byte: u8) -> u8 {
1212        match (self.config.escape, byte) {
1213            (Escape::Backslash, b'\t') => b't',
1214            (Escape::Backslash, b'\n') => b'n',
1215            (Escape::Backslash, b'\r') => b'r',
1216            (_, byte) => byte,
1217        }
1218    }
1219
1220    /// Returns the text of an atom, `None` for null.
1221    ///
1222    /// Text that is not a string is written into `scratch`.
1223    fn text<'a>(
1224        &self,
1225        atom: &'a Atom<'_>,
1226        scratch: &'a mut Vec<u8>,
1227    ) -> Result<Option<Text<'a>>, Error> {
1228        scratch.clear();
1229        let numeric = match *atom {
1230            Atom::Null => return Ok(None),
1231            Atom::Bool(value) => {
1232                let text: &[u8] = if value { b"true" } else { b"false" };
1233                return Ok(Some(Text {
1234                    bytes: text,
1235                    numeric: false,
1236                }));
1237            }
1238            Atom::Str(ref value) | Atom::Lexical(ref value) => {
1239                return Ok(Some(Text {
1240                    bytes: value.as_bytes(),
1241                    numeric: false,
1242                }));
1243            }
1244            Atom::Char(value) => {
1245                scratch.extend_from_slice(value.encode_utf8(&mut [0; 4]).as_bytes());
1246                false
1247            }
1248            Atom::U64(value) => {
1249                scratch.extend_from_slice(IntBuffer::new().format_u64(value).as_bytes());
1250                true
1251            }
1252            Atom::I64(value) => {
1253                scratch.extend_from_slice(IntBuffer::new().format_i64(value).as_bytes());
1254                true
1255            }
1256            Atom::F32(value) => {
1257                write_float(scratch, value);
1258                true
1259            }
1260            Atom::F64(value) => {
1261                write_float(scratch, value);
1262                true
1263            }
1264            Atom::Bytes(ref bytes) => {
1265                let format = bytes.fallback.copied().unwrap_or(self.config.bytes);
1266                let text = format
1267                    .encode(bytes)
1268                    .or_else(|| BytesFormat::BASE64.encode(bytes))
1269                    .unwrap_or_default();
1270                scratch.extend_from_slice(text.as_bytes());
1271                false
1272            }
1273            Atom::Ext(ref ext) => {
1274                if let Some(number) = ext.downcast_value_ref::<Number>() {
1275                    // numbers keep their text
1276                    scratch.extend_from_slice(number.as_str().as_bytes());
1277                } else if let Some(value) = ext.downcast_ref::<u128>() {
1278                    let _ = write!(ByteWriter(scratch), "{}", value);
1279                } else if let Some(value) = ext.downcast_ref::<i128>() {
1280                    let _ = write!(ByteWriter(scratch), "{}", value);
1281                } else {
1282                    return match ext.fallback() {
1283                        Atom::Ext(_) => Err(Error::new(
1284                            ErrorKind::UnsupportedType,
1285                            format!("CSV does not support {}", ext.name()),
1286                        )),
1287                        fallback => {
1288                            let mut inner = Vec::new();
1289                            let numeric = match self.text(&fallback, &mut inner)? {
1290                                Some(text) => {
1291                                    let numeric = text.numeric;
1292                                    scratch.extend_from_slice(text.bytes);
1293                                    numeric
1294                                }
1295                                None => return Ok(None),
1296                            };
1297                            Ok(Some(Text {
1298                                bytes: scratch,
1299                                numeric,
1300                            }))
1301                        }
1302                    };
1303                }
1304                true
1305            }
1306            // values whose type was inferred from text are written as value
1307            Atom::Implicit(ref value) => {
1308                let mut inner = Vec::new();
1309                return Ok(match self.text(&value.value().to_atom(), &mut inner)? {
1310                    Some(text) => {
1311                        let numeric = text.numeric;
1312                        scratch.extend_from_slice(text.bytes);
1313                        Some(Text {
1314                            bytes: scratch,
1315                            numeric,
1316                        })
1317                    }
1318                    None => None,
1319                });
1320            }
1321            _ => {
1322                return Err(Error::new(
1323                    ErrorKind::UnsupportedType,
1324                    format!("CSV does not support {}", atom.name()),
1325                ));
1326            }
1327        };
1328        Ok(Some(Text {
1329            bytes: scratch,
1330            numeric,
1331        }))
1332    }
1333}
1334
1335/// Formats into a byte buffer (`std::io::Write` is not in `core`).
1336struct ByteWriter<'a>(&'a mut Vec<u8>);
1337
1338impl fmt::Write for ByteWriter<'_> {
1339    fn write_str(&mut self, s: &str) -> fmt::Result {
1340        self.0.extend_from_slice(s.as_bytes());
1341        Ok(())
1342    }
1343}
1344
1345/// Writes a float with the shortest text that reads back as the same value
1346/// of its type (`f32` or `f64`), like the other formats.
1347fn write_float<F: Float>(out: &mut Vec<u8>, value: F) {
1348    if value.is_finite() {
1349        out.extend_from_slice(format_finite(value).as_bytes());
1350    } else {
1351        // `NaN`, `inf` and `-inf`
1352        let _ = write!(ByteWriter(out), "{}", value.to_f64());
1353    }
1354}
1355
1356#[cold]
1357fn unsupported_key() -> Error {
1358    Error::new(
1359        ErrorKind::UnsupportedType,
1360        "the keys of records must be strings, numbers or booleans",
1361    )
1362}