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 comment: None,
30 };
31 WriterBuilder { wtr: wtr }
32 }
33
34 /// Builder a CSV writer from this configuration.
build(&self) -> Writer35 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','`.
delimiter(&mut self, delimiter: u8) -> &mut WriterBuilder73 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.
terminator(&mut self, term: Terminator) -> &mut WriterBuilder84 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.
quote_style(&mut self, style: QuoteStyle) -> &mut WriterBuilder96 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'"'`.
quote(&mut self, quote: u8) -> &mut WriterBuilder104 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'\\'`.
escape(&mut self, escape: u8) -> &mut WriterBuilder114 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).
double_quote(&mut self, yes: bool) -> &mut WriterBuilder126 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.
comment(&mut self, comment: Option<u8>) -> &mut WriterBuilder137 pub fn comment(&mut self, comment: Option<u8>) -> &mut WriterBuilder {
138 self.wtr.comment = comment;
139 self
140 }
141 }
142
143 impl Default for WriterBuilder {
default() -> WriterBuilder144 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)]
156 pub 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.
179 pub 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
191 impl Clone for Writer {
clone(&self) -> Writer192 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
211 impl fmt::Debug for Writer {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result212 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)]
226 struct 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
242 impl Writer {
243 /// Creates a new CSV writer with the default configuration.
new() -> Writer244 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.
finish(&mut self, mut output: &mut [u8]) -> (WriteResult, usize)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.
field( &mut self, input: &[u8], mut output: &mut [u8], ) -> (WriteResult, usize, usize)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).
delimiter( &mut self, mut output: &mut [u8], ) -> (WriteResult, usize)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`).
terminator( &mut self, mut output: &mut [u8], ) -> (WriteResult, usize)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]
needs_quotes(&self, mut input: &[u8]) -> bool414 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]
is_special_byte(&self, b: u8) -> bool436 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]
should_quote(&self, input: &[u8]) -> bool443 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]
get_delimiter(&self) -> u8455 pub fn get_delimiter(&self) -> u8 {
456 self.delimiter
457 }
458
459 /// Return the terminator used for this writer.
460 #[inline]
get_terminator(&self) -> Terminator461 pub fn get_terminator(&self) -> Terminator {
462 self.term
463 }
464
465 /// Return the quoting style used for this writer.
466 #[inline]
get_quote_style(&self) -> QuoteStyle467 pub fn get_quote_style(&self) -> QuoteStyle {
468 self.style
469 }
470
471 /// Return the quote character used for this writer.
472 #[inline]
get_quote(&self) -> u8473 pub fn get_quote(&self) -> u8 {
474 self.quote
475 }
476
477 /// Return the escape character used for this writer.
478 #[inline]
get_escape(&self) -> u8479 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]
get_double_quote(&self) -> bool486 pub fn get_double_quote(&self) -> bool {
487 self.double_quote
488 }
489
write(&self, data: &[u8], output: &mut [u8]) -> (WriteResult, usize)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
500 impl Default for Writer {
default() -> Writer501 fn default() -> Writer {
502 WriterBuilder::new().build()
503 }
504 }
505
506 impl Default for WriterState {
default() -> WriterState507 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.
is_non_numeric(input: &[u8]) -> bool513 pub 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`.
quote( mut input: &[u8], mut output: &mut [u8], quote: u8, escape: u8, double_quote: bool, ) -> (WriteResult, usize, usize)548 pub 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`.
write_optimistic( input: &[u8], output: &mut [u8], ) -> (WriteResult, usize, usize)607 fn 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`.
write_pessimistic(input: &[u8], output: &mut [u8]) -> (WriteResult, usize)626 fn 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/
moving<T>(x: T) -> T637 fn moving<T>(x: T) -> T {
638 x
639 }
640
641 #[cfg(test)]
642 mod tests {
643 use crate::writer::WriteResult::*;
644 use crate::writer::{quote, QuoteStyle, Writer, WriterBuilder};
645
646 // OMG I HATE BYTE STRING LITERALS SO MUCH.
b(s: &str) -> &[u8]647 fn b(s: &str) -> &[u8] {
648 s.as_bytes()
649 }
s(b: &[u8]) -> &str650 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]
writer_one_field()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]
writer_one_empty_field_terminator()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]
writer_one_empty_field_finish()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]
writer_many_one_empty_field_finish()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]
writer_many_one_empty_field_terminator()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]
writer_one_field_quote()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]
writer_one_field_stream()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]
writer_one_field_stream_quote()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]
writer_one_field_stream_quote_partial()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]
writer_two_fields()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]
writer_two_fields_non_numeric()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]
writer_two_fields_quote()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]
writer_two_fields_two_records()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]
writer_two_fields_two_records_quote()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]
quote_empty()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]
quote_no_quotes()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]
quote_one_quote()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]
quote_two_quotes()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]
quote_escaped_one()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]
quote_escaped_two()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]
quote_misc()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]
quote_stream_no_quotes()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]
quote_stream_quotes()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]
comment_char_is_automatically_quoted()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 }
1086