csv_core/
writer.rs

1use core::fmt;
2use core::str;
3
4use memchr::memchr;
5
6use crate::{QuoteStyle, Terminator};
7
8/// A builder for configuring a CSV writer.
9///
10/// This builder permits specifying the CSV delimiter, terminator, quoting
11/// style and more.
12#[derive(Debug)]
13pub struct WriterBuilder {
14    wtr: Writer,
15}
16
17impl WriterBuilder {
18    /// Create a new builder for configuring a CSV writer.
19    pub fn new() -> WriterBuilder {
20        let wtr = Writer {
21            state: WriterState::default(),
22            requires_quotes: [false; 256],
23            delimiter: b',',
24            term: Terminator::Any(b'\n'),
25            style: QuoteStyle::default(),
26            quote: b'"',
27            escape: b'\\',
28            double_quote: true,
29            comment: None,
30        };
31        WriterBuilder { wtr: wtr }
32    }
33
34    /// Builder a CSV writer from this configuration.
35    pub fn build(&self) -> Writer {
36        use crate::Terminator::*;
37
38        let mut wtr = self.wtr.clone();
39        wtr.requires_quotes[self.wtr.delimiter as usize] = true;
40        wtr.requires_quotes[self.wtr.quote as usize] = true;
41        if !self.wtr.double_quote {
42            // We only need to quote the escape character if the escape
43            // character is used for escaping quotes.
44            wtr.requires_quotes[self.wtr.escape as usize] = true;
45        }
46        match self.wtr.term {
47            CRLF | Any(b'\n') | Any(b'\r') => {
48                // This is a bit hokey. By default, the record terminator
49                // is '\n', but we still need to quote '\r' (even if our
50                // terminator is only `\n`) because the reader interprets '\r'
51                // as a record terminator by default.
52                wtr.requires_quotes[b'\r' as usize] = true;
53                wtr.requires_quotes[b'\n' as usize] = true;
54            }
55            Any(b) => {
56                wtr.requires_quotes[b as usize] = true;
57            }
58            _ => unreachable!(),
59        }
60        // If the first field of a row starts with a comment character,
61        // it needs to be quoted, or the row will not be readable later.
62        // As requires_quotes is calculated in advance, we force quotes
63        // when a comment character is encountered anywhere in the field.
64        if let Some(comment) = self.wtr.comment {
65            wtr.requires_quotes[comment as usize] = true;
66        }
67        wtr
68    }
69
70    /// The field delimiter to use when writing CSV.
71    ///
72    /// The default is `b','`.
73    pub fn delimiter(&mut self, delimiter: u8) -> &mut WriterBuilder {
74        self.wtr.delimiter = delimiter;
75        self
76    }
77
78    /// The record terminator to use when writing CSV.
79    ///
80    /// A record terminator can be any single byte. The default is `\n`.
81    ///
82    /// Note that RFC 4180 specifies that record terminators should be `\r\n`.
83    /// To use `\r\n`, use the special `Terminator::CRLF` value.
84    pub fn terminator(&mut self, term: Terminator) -> &mut WriterBuilder {
85        self.wtr.term = term;
86        self
87    }
88
89    /// The quoting style to use when writing CSV.
90    ///
91    /// By default, this is set to `QuoteStyle::Necessary`, which will only
92    /// use quotes when they are necessary to preserve the integrity of data.
93    ///
94    /// Note that unless the quote style is set to `Never`, an empty field is
95    /// quoted if it is the only field in a record.
96    pub fn quote_style(&mut self, style: QuoteStyle) -> &mut WriterBuilder {
97        self.wtr.style = style;
98        self
99    }
100
101    /// The quote character to use when writing CSV.
102    ///
103    /// The default value is `b'"'`.
104    pub fn quote(&mut self, quote: u8) -> &mut WriterBuilder {
105        self.wtr.quote = quote;
106        self
107    }
108
109    /// The escape character to use when writing CSV.
110    ///
111    /// This is only used when `double_quote` is set to `false`.
112    ///
113    /// The default value is `b'\\'`.
114    pub fn escape(&mut self, escape: u8) -> &mut WriterBuilder {
115        self.wtr.escape = escape;
116        self
117    }
118
119    /// The quoting escape mechanism to use when writing CSV.
120    ///
121    /// When enabled (which is the default), quotes are escaped by doubling
122    /// them. e.g., `"` escapes to `""`.
123    ///
124    /// When disabled, quotes are escaped with the escape character (which
125    /// is `\\` by default).
126    pub fn double_quote(&mut self, yes: bool) -> &mut WriterBuilder {
127        self.wtr.double_quote = yes;
128        self
129    }
130
131    /// The comment character that will be used when later reading the file.
132    ///
133    /// If `quote_style` is set to `QuoteStyle::Necessary`, a field will
134    /// be quoted if the comment character is detected anywhere in the field.
135    ///
136    /// The default value is None.
137    pub fn comment(&mut self, comment: Option<u8>) -> &mut WriterBuilder {
138        self.wtr.comment = comment;
139        self
140    }
141}
142
143impl Default for WriterBuilder {
144    fn default() -> WriterBuilder {
145        WriterBuilder::new()
146    }
147}
148
149/// The result of writing CSV data.
150///
151/// A value of this type is returned from every interaction with `Writer`. It
152/// informs the caller how to proceed, namely, by indicating whether more
153/// input should be given (`InputEmpty`) or if a bigger output buffer is needed
154/// (`OutputFull`).
155#[derive(Clone, Debug, Eq, PartialEq)]
156pub enum WriteResult {
157    /// This result occurs when all of the bytes from the given input have
158    /// been processed.
159    InputEmpty,
160    /// This result occurs when the output buffer was too small to process
161    /// all of the input bytes. Generally, this means the caller must call
162    /// the corresponding method again with the rest of the input and more
163    /// room in the output buffer.
164    OutputFull,
165}
166
167/// A writer for CSV data.
168///
169/// # RFC 4180
170///
171/// This writer conforms to RFC 4180 with one exception: it doesn't guarantee
172/// that all records written are of the same length. Instead, the onus is on
173/// the caller to ensure that all records written are of the same length.
174///
175/// Note that the default configuration of a `Writer` uses `\n` for record
176/// terminators instead of `\r\n` as specified by RFC 4180. Use the
177/// `terminator` method on `WriterBuilder` to set the terminator to `\r\n` if
178/// it's desired.
179pub struct Writer {
180    state: WriterState,
181    requires_quotes: [bool; 256],
182    delimiter: u8,
183    term: Terminator,
184    style: QuoteStyle,
185    quote: u8,
186    escape: u8,
187    double_quote: bool,
188    comment: Option<u8>,
189}
190
191impl Clone for Writer {
192    fn clone(&self) -> Writer {
193        let mut requires_quotes = [false; 256];
194        for i in 0..256 {
195            requires_quotes[i] = self.requires_quotes[i];
196        }
197        Writer {
198            state: self.state.clone(),
199            requires_quotes: requires_quotes,
200            delimiter: self.delimiter,
201            term: self.term,
202            style: self.style,
203            quote: self.quote,
204            escape: self.escape,
205            double_quote: self.double_quote,
206            comment: self.comment,
207        }
208    }
209}
210
211impl fmt::Debug for Writer {
212    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213        f.debug_struct("Writer")
214            .field("state", &self.state)
215            .field("delimiter", &self.delimiter)
216            .field("term", &self.term)
217            .field("style", &self.style)
218            .field("quote", &self.quote)
219            .field("escape", &self.escape)
220            .field("double_quote", &self.double_quote)
221            .finish()
222    }
223}
224
225#[derive(Clone, Debug)]
226struct WriterState {
227    /// This is set whenever we've begun writing the contents of a field, even
228    /// if the contents are empty. We use it to avoid re-computing whether
229    /// quotes are necessary.
230    in_field: bool,
231    /// This is set whenever we've started writing a field that is enclosed in
232    /// quotes. When the writer is finished, or if a delimiter or terminator
233    /// are written, then a closing quote is inserted when this is true.
234    quoting: bool,
235    /// The number of total bytes written for the current record.
236    ///
237    /// If the writer is finished or a terminator is written when this is `0`,
238    /// then an empty field is added as a pair of adjacent quotes.
239    record_bytes: u64,
240}
241
242impl Writer {
243    /// Creates a new CSV writer with the default configuration.
244    pub fn new() -> Writer {
245        Writer::default()
246    }
247
248    /// Finish writing CSV data to `output`.
249    ///
250    /// This must be called when one is done writing CSV data to `output`.
251    /// In particular, it will write closing quotes if necessary.
252    pub fn finish(&mut self, mut output: &mut [u8]) -> (WriteResult, usize) {
253        let mut nout = 0;
254        if self.state.record_bytes == 0 && self.state.in_field {
255            assert!(!self.state.quoting);
256            let (res, o) = self.write(&[self.quote, self.quote], output);
257            if o == 0 {
258                return (res, 0);
259            }
260            output = &mut moving(output)[o..];
261            nout += o;
262            self.state.record_bytes += o as u64;
263        }
264        if !self.state.quoting {
265            return (WriteResult::InputEmpty, nout);
266        }
267        let (res, o) = self.write(&[self.quote], output);
268        if o == 0 {
269            return (res, nout);
270        }
271        nout += o;
272        self.state.record_bytes = 0;
273        self.state.in_field = false;
274        self.state.quoting = false;
275        (res, nout)
276    }
277
278    /// Write a single CSV field from `input` to `output` while employing this
279    /// writer's quoting style.
280    ///
281    /// This returns the result of writing field data, in addition to the
282    /// number of bytes consumed from `input` and the number of bytes
283    /// written to `output`.
284    ///
285    /// The result of writing field data is either `WriteResult::InputEmpty`
286    /// or `WriteResult::OutputFull`. The former occurs when all bytes in
287    /// `input` were copied to `output`, while the latter occurs when `output`
288    /// is too small to fit everything from `input`. The maximum number of
289    /// bytes that can be written to `output` is `2 + (2 * input.len())`
290    /// because of quoting. (The worst case is a field consisting entirely
291    /// of quotes.)
292    ///
293    /// Multiple successive calls to `field` will write more data to the same
294    /// field. Subsequent fields can be written by calling either `delimiter`
295    /// or `terminator` first.
296    ///
297    /// If this writer's quoting style is `QuoteStyle::Necessary`, then `input`
298    /// should contain the *entire* field. Otherwise, whether the field needs
299    /// to be quoted or not cannot be determined.
300    pub fn field(
301        &mut self,
302        input: &[u8],
303        mut output: &mut [u8],
304    ) -> (WriteResult, usize, usize) {
305        let (mut nin, mut nout) = (0, 0);
306
307        if !self.state.in_field {
308            self.state.quoting = self.should_quote(input);
309            if self.state.quoting {
310                let (res, o) = self.write(&[self.quote], output);
311                if o == 0 {
312                    return (res, 0, 0);
313                }
314                output = &mut moving(output)[o..];
315                nout += o;
316                self.state.record_bytes += o as u64;
317            }
318            self.state.in_field = true;
319        }
320        let (res, i, o) = if self.state.quoting {
321            quote(input, output, self.quote, self.escape, self.double_quote)
322        } else {
323            write_optimistic(input, output)
324        };
325        nin += i;
326        nout += o;
327        self.state.record_bytes += o as u64;
328        (res, nin, nout)
329    }
330
331    /// Write the configured field delimiter to `output`.
332    ///
333    /// If the output buffer does not have enough room to fit
334    /// a field delimiter, then nothing is written to `output`
335    /// and `WriteResult::OutputFull` is returned. Otherwise,
336    /// `WriteResult::InputEmpty` is returned along with the number of bytes
337    /// written to `output` (which is `1` in case of an unquoted
338    /// field, or `2` in case of an end quote and a field separator).
339    pub fn delimiter(
340        &mut self,
341        mut output: &mut [u8],
342    ) -> (WriteResult, usize) {
343        let mut nout = 0;
344        if self.state.quoting {
345            let (res, o) = self.write(&[self.quote], output);
346            if o == 0 {
347                return (res, o);
348            }
349            output = &mut moving(output)[o..];
350            nout += o;
351            self.state.record_bytes += o as u64;
352            self.state.quoting = false;
353        }
354        let (res, o) = self.write(&[self.delimiter], output);
355        if o == 0 {
356            return (res, nout);
357        }
358        nout += o;
359        self.state.record_bytes += o as u64;
360        self.state.in_field = false;
361        (res, nout)
362    }
363
364    /// Write the configured record terminator to `output`.
365    ///
366    /// If the output buffer does not have enough room to fit a record
367    /// terminator, then no part of the terminator is written and
368    /// `WriteResult::OutputFull` is returned. Otherwise,
369    /// `WriteResult::InputEmpty` is returned along with the number of bytes
370    /// written to `output` (which is always `1` or `2`).
371    pub fn terminator(
372        &mut self,
373        mut output: &mut [u8],
374    ) -> (WriteResult, usize) {
375        let mut nout = 0;
376        if self.state.record_bytes == 0 {
377            assert!(!self.state.quoting);
378            let (res, o) = self.write(&[self.quote, self.quote], output);
379            if o == 0 {
380                return (res, 0);
381            }
382            output = &mut moving(output)[o..];
383            nout += o;
384            self.state.record_bytes += o as u64;
385        }
386        if self.state.quoting {
387            let (res, o) = self.write(&[self.quote], output);
388            if o == 0 {
389                return (res, o);
390            }
391            output = &mut moving(output)[o..];
392            nout += o;
393            self.state.record_bytes += o as u64;
394            self.state.quoting = false;
395        }
396        let (res, o) = match self.term {
397            Terminator::CRLF => write_pessimistic(&[b'\r', b'\n'], output),
398            Terminator::Any(b) => write_pessimistic(&[b], output),
399            _ => unreachable!(),
400        };
401        if o == 0 {
402            return (res, nout);
403        }
404        nout += o;
405        self.state.record_bytes = 0;
406        self.state.in_field = false;
407        (res, nout)
408    }
409
410    /// Returns true if and only if the given input field *requires* quotes to
411    /// preserve the integrity of `input` while taking into account the current
412    /// configuration of this writer (except for the configured quoting style).
413    #[inline]
414    fn needs_quotes(&self, mut input: &[u8]) -> bool {
415        let mut needs = false;
416        while !needs && input.len() >= 8 {
417            needs = self.requires_quotes[input[0] as usize]
418                || self.requires_quotes[input[1] as usize]
419                || self.requires_quotes[input[2] as usize]
420                || self.requires_quotes[input[3] as usize]
421                || self.requires_quotes[input[4] as usize]
422                || self.requires_quotes[input[5] as usize]
423                || self.requires_quotes[input[6] as usize]
424                || self.requires_quotes[input[7] as usize];
425            input = &input[8..];
426        }
427        needs || input.iter().any(|&b| self.is_special_byte(b))
428    }
429
430    /// Returns true if and only if the given byte corresponds to a special
431    /// byte in this CSV writer's configuration.
432    ///
433    /// Note that this does **not** take into account this writer's quoting
434    /// style.
435    #[inline]
436    pub fn is_special_byte(&self, b: u8) -> bool {
437        self.requires_quotes[b as usize]
438    }
439
440    /// Returns true if and only if we should put the given field data
441    /// in quotes. This takes the quoting style into account.
442    #[inline]
443    pub fn should_quote(&self, input: &[u8]) -> bool {
444        match self.style {
445            QuoteStyle::Always => true,
446            QuoteStyle::Never => false,
447            QuoteStyle::NonNumeric => is_non_numeric(input),
448            QuoteStyle::Necessary => self.needs_quotes(input),
449            _ => unreachable!(),
450        }
451    }
452
453    /// Return the delimiter used for this writer.
454    #[inline]
455    pub fn get_delimiter(&self) -> u8 {
456        self.delimiter
457    }
458
459    /// Return the terminator used for this writer.
460    #[inline]
461    pub fn get_terminator(&self) -> Terminator {
462        self.term
463    }
464
465    /// Return the quoting style used for this writer.
466    #[inline]
467    pub fn get_quote_style(&self) -> QuoteStyle {
468        self.style
469    }
470
471    /// Return the quote character used for this writer.
472    #[inline]
473    pub fn get_quote(&self) -> u8 {
474        self.quote
475    }
476
477    /// Return the escape character used for this writer.
478    #[inline]
479    pub fn get_escape(&self) -> u8 {
480        self.escape
481    }
482
483    /// Return whether this writer doubles quotes or not. When the writer
484    /// does not double quotes, it will escape them using the escape character.
485    #[inline]
486    pub fn get_double_quote(&self) -> bool {
487        self.double_quote
488    }
489
490    fn write(&self, data: &[u8], output: &mut [u8]) -> (WriteResult, usize) {
491        if data.len() > output.len() {
492            (WriteResult::OutputFull, 0)
493        } else {
494            output[..data.len()].copy_from_slice(data);
495            (WriteResult::InputEmpty, data.len())
496        }
497    }
498}
499
500impl Default for Writer {
501    fn default() -> Writer {
502        WriterBuilder::new().build()
503    }
504}
505
506impl Default for WriterState {
507    fn default() -> WriterState {
508        WriterState { in_field: false, quoting: false, record_bytes: 0 }
509    }
510}
511
512/// Returns true if and only if the given input is non-numeric.
513pub fn is_non_numeric(input: &[u8]) -> bool {
514    let s = match str::from_utf8(input) {
515        Err(_) => return true,
516        Ok(s) => s,
517    };
518    // I suppose this could be faster if we wrote validators of numbers instead
519    // of using the actual parser, but that's probably a lot of work for a bit
520    // of a niche feature.
521    !s.parse::<f64>().is_ok() && !s.parse::<i128>().is_ok()
522}
523
524/// Escape quotes `input` and writes the result to `output`.
525///
526/// If `input` does not have a `quote`, then the contents of `input` are
527/// copied verbatim to `output`.
528///
529/// If `output` is not big enough to store the fully quoted contents of
530/// `input`, then `WriteResult::OutputFull` is returned. The `output` buffer
531/// will require a maximum of storage of `2 * input.len()` in the worst case
532/// (where every byte is a quote).
533///
534/// In streaming contexts, `quote` should be called in a loop until
535/// `WriteResult::InputEmpty` is returned. It is possible to write an infinite
536/// loop if your output buffer is less than 2 bytes in length (the minimum
537/// storage space required to store an escaped quote).
538///
539/// In addition to the `WriteResult`, the number of consumed bytes from `input`
540/// and the number of bytes written to `output` are also returned.
541///
542/// `quote` is the quote byte and `escape` is the escape byte. If
543/// `double_quote` is true, then quotes are escaped by doubling them,
544/// otherwise, quotes are escaped with the `escape` byte.
545///
546/// N.B. This function is provided for low level usage. It is called
547/// automatically if you're using a `Writer`.
548pub fn quote(
549    mut input: &[u8],
550    mut output: &mut [u8],
551    quote: u8,
552    escape: u8,
553    double_quote: bool,
554) -> (WriteResult, usize, usize) {
555    let (mut nin, mut nout) = (0, 0);
556    loop {
557        match memchr(quote, input) {
558            None => {
559                let (res, i, o) = write_optimistic(input, output);
560                nin += i;
561                nout += o;
562                return (res, nin, nout);
563            }
564            Some(next_quote) => {
565                let (res, i, o) =
566                    write_optimistic(&input[..next_quote], output);
567                input = &input[i..];
568                output = &mut moving(output)[o..];
569                nin += i;
570                nout += o;
571                if let WriteResult::OutputFull = res {
572                    return (res, nin, nout);
573                }
574                if double_quote {
575                    let (res, o) = write_pessimistic(&[quote, quote], output);
576                    if let WriteResult::OutputFull = res {
577                        return (res, nin, nout);
578                    }
579                    nout += o;
580                    output = &mut moving(output)[o..];
581                } else {
582                    let (res, o) = write_pessimistic(&[escape, quote], output);
583                    if let WriteResult::OutputFull = res {
584                        return (res, nin, nout);
585                    }
586                    nout += o;
587                    output = &mut moving(output)[o..];
588                }
589                nin += 1;
590                input = &input[1..];
591            }
592        }
593    }
594}
595
596/// Copy the bytes from `input` to `output`. If `output` is too small to fit
597/// everything from `input`, then copy `output.len()` bytes from `input`.
598/// Otherwise, copy everything from `input` into `output`.
599///
600/// In the first case (`output` is too small), `WriteResult::OutputFull` is
601/// returned, in addition to the number of bytes consumed from `input` and
602/// the number of bytes written to `output`.
603///
604/// In the second case (`input` is no bigger than `output`),
605/// `WriteResult::InputEmpty` is returned, in addition to the number of bytes
606/// consumed from `input` and the number of bytes written to `output`.
607fn write_optimistic(
608    input: &[u8],
609    output: &mut [u8],
610) -> (WriteResult, usize, usize) {
611    if input.len() > output.len() {
612        let input = &input[..output.len()];
613        output.copy_from_slice(input);
614        (WriteResult::OutputFull, output.len(), output.len())
615    } else {
616        output[..input.len()].copy_from_slice(input);
617        (WriteResult::InputEmpty, input.len(), input.len())
618    }
619}
620
621/// Copy the bytes from `input` to `output` only if `input` is no bigger than
622/// `output`. If `input` is bigger than `output`, then return
623/// `WriteResult::OutputFull` and copy nothing into `output`. Otherwise,
624/// return `WriteResult::InputEmpty` and the number of bytes copied into
625/// `output`.
626fn write_pessimistic(input: &[u8], output: &mut [u8]) -> (WriteResult, usize) {
627    if input.len() > output.len() {
628        (WriteResult::OutputFull, 0)
629    } else {
630        output[..input.len()].copy_from_slice(input);
631        (WriteResult::InputEmpty, input.len())
632    }
633}
634
635/// This avoids reborrowing.
636/// See: https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/
637fn moving<T>(x: T) -> T {
638    x
639}
640
641#[cfg(test)]
642mod tests {
643    use crate::writer::WriteResult::*;
644    use crate::writer::{quote, QuoteStyle, Writer, WriterBuilder};
645
646    // OMG I HATE BYTE STRING LITERALS SO MUCH.
647    fn b(s: &str) -> &[u8] {
648        s.as_bytes()
649    }
650    fn s(b: &[u8]) -> &str {
651        ::core::str::from_utf8(b).unwrap()
652    }
653
654    macro_rules! assert_field {
655        (
656            $wtr:expr, $inp:expr, $out:expr,
657            $expect_in:expr, $expect_out:expr,
658            $expect_res:expr, $expect_data:expr
659        ) => {{
660            let (res, i, o) = $wtr.field($inp, $out);
661            assert_eq!($expect_res, res, "result");
662            assert_eq!($expect_in, i, "input");
663            assert_eq!($expect_out, o, "output");
664            assert_eq!($expect_data, s(&$out[..o]), "data");
665        }};
666    }
667
668    macro_rules! assert_write {
669        (
670            $wtr:expr, $which:ident, $out:expr,
671            $expect_out:expr, $expect_res:expr, $expect_data:expr
672        ) => {{
673            let (res, o) = $wtr.$which($out);
674            assert_eq!($expect_res, res, "result");
675            assert_eq!($expect_out, o, "output");
676            assert_eq!($expect_data, s(&$out[..o]), "data");
677        }};
678    }
679
680    #[test]
681    fn writer_one_field() {
682        let mut wtr = Writer::new();
683        let out = &mut [0; 1024];
684        let mut n = 0;
685
686        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
687        n += 3;
688
689        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
690    }
691
692    #[test]
693    fn writer_one_empty_field_terminator() {
694        let mut wtr = Writer::new();
695        let out = &mut [0; 1024];
696
697        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
698        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
699        assert_write!(wtr, finish, &mut out[..], 0, InputEmpty, "");
700    }
701
702    #[test]
703    fn writer_one_empty_field_finish() {
704        let mut wtr = Writer::new();
705        let out = &mut [0; 1024];
706
707        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
708        assert_write!(wtr, finish, &mut out[..], 2, InputEmpty, "\"\"");
709    }
710
711    #[test]
712    fn writer_many_one_empty_field_finish() {
713        let mut wtr = Writer::new();
714        let out = &mut [0; 1024];
715
716        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
717        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
718        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
719        assert_write!(wtr, finish, &mut out[..], 2, InputEmpty, "\"\"");
720    }
721
722    #[test]
723    fn writer_many_one_empty_field_terminator() {
724        let mut wtr = Writer::new();
725        let out = &mut [0; 1024];
726
727        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
728        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
729        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
730        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
731        assert_write!(wtr, finish, &mut out[..], 0, InputEmpty, "");
732    }
733
734    #[test]
735    fn writer_one_field_quote() {
736        let mut wtr = Writer::new();
737        let out = &mut [0; 1024];
738        let mut n = 0;
739
740        assert_field!(
741            wtr,
742            b("a\"bc"),
743            &mut out[n..],
744            4,
745            6,
746            InputEmpty,
747            "\"a\"\"bc"
748        );
749        n += 6;
750
751        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
752    }
753
754    #[test]
755    fn writer_one_field_stream() {
756        let mut wtr = Writer::new();
757        let out = &mut [0; 1024];
758        let mut n = 0;
759
760        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
761        n += 3;
762        assert_field!(wtr, b("x"), &mut out[n..], 1, 1, InputEmpty, "x");
763        n += 1;
764
765        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
766    }
767
768    #[test]
769    fn writer_one_field_stream_quote() {
770        let mut wtr = Writer::new();
771        let out = &mut [0; 1024];
772        let mut n = 0;
773
774        assert_field!(
775            wtr,
776            b("abc\""),
777            &mut out[n..],
778            4,
779            6,
780            InputEmpty,
781            "\"abc\"\""
782        );
783        n += 6;
784        assert_field!(wtr, b("x"), &mut out[n..], 1, 1, InputEmpty, "x");
785        n += 1;
786
787        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
788    }
789
790    #[test]
791    fn writer_one_field_stream_quote_partial() {
792        let mut wtr = Writer::new();
793        let out = &mut [0; 4];
794
795        assert_field!(wtr, b("ab\"xyz"), out, 2, 3, OutputFull, "\"ab");
796        assert_field!(wtr, b("\"xyz"), out, 3, 4, OutputFull, "\"\"xy");
797        assert_field!(wtr, b("z"), out, 1, 1, InputEmpty, "z");
798        assert_write!(wtr, finish, out, 1, InputEmpty, "\"");
799    }
800
801    #[test]
802    fn writer_two_fields() {
803        let mut wtr = Writer::new();
804        let out = &mut [0; 1024];
805        let mut n = 0;
806
807        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
808        n += 3;
809        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
810        n += 1;
811        assert_field!(wtr, b("yz"), &mut out[n..], 2, 2, InputEmpty, "yz");
812        n += 2;
813
814        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
815
816        assert_eq!("abc,yz", s(&out[..n]));
817    }
818
819    #[test]
820    fn writer_two_fields_non_numeric() {
821        let mut wtr =
822            WriterBuilder::new().quote_style(QuoteStyle::NonNumeric).build();
823        let out = &mut [0; 1024];
824        let mut n = 0;
825
826        assert_field!(wtr, b("abc"), &mut out[n..], 3, 4, InputEmpty, "\"abc");
827        n += 4;
828        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
829        n += 2;
830        assert_field!(wtr, b("5.2"), &mut out[n..], 3, 3, InputEmpty, "5.2");
831        n += 3;
832        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
833        n += 1;
834        assert_field!(wtr, b("98"), &mut out[n..], 2, 2, InputEmpty, "98");
835        n += 2;
836
837        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
838
839        assert_eq!("\"abc\",5.2,98", s(&out[..n]));
840    }
841
842    #[test]
843    fn writer_two_fields_quote() {
844        let mut wtr = Writer::new();
845        let out = &mut [0; 1024];
846        let mut n = 0;
847
848        assert_field!(
849            wtr,
850            b("a,bc"),
851            &mut out[n..],
852            4,
853            5,
854            InputEmpty,
855            "\"a,bc"
856        );
857        n += 5;
858        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
859        n += 2;
860        assert_field!(wtr, b("\nz"), &mut out[n..], 2, 3, InputEmpty, "\"\nz");
861        n += 3;
862
863        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
864        n += 1;
865
866        assert_eq!("\"a,bc\",\"\nz\"", s(&out[..n]));
867    }
868
869    #[test]
870    fn writer_two_fields_two_records() {
871        let mut wtr = Writer::new();
872        let out = &mut [0; 1024];
873        let mut n = 0;
874
875        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
876        n += 3;
877        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
878        n += 1;
879        assert_field!(wtr, b("yz"), &mut out[n..], 2, 2, InputEmpty, "yz");
880        n += 2;
881        assert_write!(wtr, terminator, &mut out[n..], 1, InputEmpty, "\n");
882        n += 1;
883        assert_field!(wtr, b("foo"), &mut out[n..], 3, 3, InputEmpty, "foo");
884        n += 3;
885        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
886        n += 1;
887        assert_field!(wtr, b("quux"), &mut out[n..], 4, 4, InputEmpty, "quux");
888        n += 4;
889
890        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
891
892        assert_eq!("abc,yz\nfoo,quux", s(&out[..n]));
893    }
894
895    #[test]
896    fn writer_two_fields_two_records_quote() {
897        let mut wtr = Writer::new();
898        let out = &mut [0; 1024];
899        let mut n = 0;
900
901        assert_field!(
902            wtr,
903            b("a,bc"),
904            &mut out[n..],
905            4,
906            5,
907            InputEmpty,
908            "\"a,bc"
909        );
910        n += 5;
911        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
912        n += 2;
913        assert_field!(wtr, b("\nz"), &mut out[n..], 2, 3, InputEmpty, "\"\nz");
914        n += 3;
915        assert_write!(wtr, terminator, &mut out[n..], 2, InputEmpty, "\"\n");
916        n += 2;
917        assert_field!(
918            wtr,
919            b("f\"oo"),
920            &mut out[n..],
921            4,
922            6,
923            InputEmpty,
924            "\"f\"\"oo"
925        );
926        n += 6;
927        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
928        n += 2;
929        assert_field!(
930            wtr,
931            b("quux,"),
932            &mut out[n..],
933            5,
934            6,
935            InputEmpty,
936            "\"quux,"
937        );
938        n += 6;
939
940        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
941        n += 1;
942
943        assert_eq!("\"a,bc\",\"\nz\"\n\"f\"\"oo\",\"quux,\"", s(&out[..n]));
944    }
945
946    macro_rules! assert_quote {
947        (
948            $inp:expr, $out:expr,
949            $expect_in:expr, $expect_out:expr,
950            $expect_res:expr, $expect_data:expr
951        ) => {
952            assert_quote!(
953                $inp,
954                $out,
955                $expect_in,
956                $expect_out,
957                $expect_res,
958                $expect_data,
959                true
960            );
961        };
962        (
963            $inp:expr, $out:expr,
964            $expect_in:expr, $expect_out:expr,
965            $expect_res:expr, $expect_data:expr,
966            $double_quote:expr
967        ) => {{
968            let (res, i, o) = quote($inp, $out, b'"', b'\\', $double_quote);
969            assert_eq!($expect_res, res, "result");
970            assert_eq!($expect_in, i, "input");
971            assert_eq!($expect_out, o, "output");
972            assert_eq!(b($expect_data), &$out[..o], "data");
973        }};
974    }
975
976    #[test]
977    fn quote_empty() {
978        let inp = b("");
979        let out = &mut [0; 1024];
980
981        assert_quote!(inp, out, 0, 0, InputEmpty, "");
982    }
983
984    #[test]
985    fn quote_no_quotes() {
986        let inp = b("foobar");
987        let out = &mut [0; 1024];
988
989        assert_quote!(inp, out, 6, 6, InputEmpty, "foobar");
990    }
991
992    #[test]
993    fn quote_one_quote() {
994        let inp = b("\"");
995        let out = &mut [0; 1024];
996
997        assert_quote!(inp, out, 1, 2, InputEmpty, r#""""#);
998    }
999
1000    #[test]
1001    fn quote_two_quotes() {
1002        let inp = b("\"\"");
1003        let out = &mut [0; 1024];
1004
1005        assert_quote!(inp, out, 2, 4, InputEmpty, r#""""""#);
1006    }
1007
1008    #[test]
1009    fn quote_escaped_one() {
1010        let inp = b("\"");
1011        let out = &mut [0; 1024];
1012
1013        assert_quote!(inp, out, 1, 2, InputEmpty, r#"\""#, false);
1014    }
1015
1016    #[test]
1017    fn quote_escaped_two() {
1018        let inp = b("\"\"");
1019        let out = &mut [0; 1024];
1020
1021        assert_quote!(inp, out, 2, 4, InputEmpty, r#"\"\""#, false);
1022    }
1023
1024    #[test]
1025    fn quote_misc() {
1026        let inp = b(r#"foo "bar" baz "quux"?"#);
1027        let out = &mut [0; 1024];
1028
1029        assert_quote!(
1030            inp,
1031            out,
1032            21,
1033            25,
1034            InputEmpty,
1035            r#"foo ""bar"" baz ""quux""?"#
1036        );
1037    }
1038
1039    #[test]
1040    fn quote_stream_no_quotes() {
1041        let mut inp = b("fooba");
1042        let out = &mut [0; 2];
1043
1044        assert_quote!(inp, out, 2, 2, OutputFull, "fo");
1045        inp = &inp[2..];
1046        assert_quote!(inp, out, 2, 2, OutputFull, "ob");
1047        inp = &inp[2..];
1048        assert_quote!(inp, out, 1, 1, InputEmpty, "a");
1049    }
1050
1051    #[test]
1052    fn quote_stream_quotes() {
1053        let mut inp = b(r#"a"bc"d""#);
1054        let out = &mut [0; 2];
1055
1056        assert_quote!(inp, out, 1, 1, OutputFull, "a");
1057        inp = &inp[1..];
1058        assert_quote!(inp, out, 1, 2, OutputFull, r#""""#);
1059        inp = &inp[1..];
1060        assert_quote!(inp, out, 2, 2, OutputFull, "bc");
1061        inp = &inp[2..];
1062        assert_quote!(inp, out, 1, 2, OutputFull, r#""""#);
1063        inp = &inp[1..];
1064        assert_quote!(inp, out, 1, 1, OutputFull, "d");
1065        inp = &inp[1..];
1066        assert_quote!(inp, out, 1, 2, InputEmpty, r#""""#);
1067    }
1068
1069    #[test]
1070    fn comment_char_is_automatically_quoted() {
1071        let mut wtr = WriterBuilder::new().comment(Some(b'#')).build();
1072        let out = &mut [0; 1024];
1073
1074        assert_field!(
1075            wtr,
1076            b("# abc"),
1077            &mut out[..],
1078            5,
1079            6,
1080            InputEmpty,
1081            "\"# abc"
1082        );
1083        assert_write!(wtr, finish, &mut out[..], 1, InputEmpty, "\"");
1084    }
1085}