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