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