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