csv/
writer.rs

1use std::{fs::File, io, path::Path, result};
2
3use {
4    csv_core::{
5        self, WriteResult, Writer as CoreWriter,
6        WriterBuilder as CoreWriterBuilder,
7    },
8    serde::Serialize,
9};
10
11use crate::{
12    byte_record::ByteRecord,
13    error::{Error, ErrorKind, IntoInnerError, Result},
14    serializer::{serialize, serialize_header},
15    {QuoteStyle, Terminator},
16};
17
18/// Builds a CSV writer with various configuration knobs.
19///
20/// This builder can be used to tweak the field delimiter, record terminator
21/// and more. Once a CSV `Writer` is built, its configuration cannot be
22/// changed.
23#[derive(Debug)]
24pub struct WriterBuilder {
25    builder: CoreWriterBuilder,
26    capacity: usize,
27    flexible: bool,
28    has_headers: bool,
29}
30
31impl Default for WriterBuilder {
32    fn default() -> WriterBuilder {
33        WriterBuilder {
34            builder: CoreWriterBuilder::default(),
35            capacity: 8 * (1 << 10),
36            flexible: false,
37            has_headers: true,
38        }
39    }
40}
41
42impl WriterBuilder {
43    /// Create a new builder for configuring CSV writing.
44    ///
45    /// To convert a builder into a writer, call one of the methods starting
46    /// with `from_`.
47    ///
48    /// # Example
49    ///
50    /// ```
51    /// use std::error::Error;
52    /// use csv::WriterBuilder;
53    ///
54    /// # fn main() { example().unwrap(); }
55    /// fn example() -> Result<(), Box<dyn Error>> {
56    ///     let mut wtr = WriterBuilder::new().from_writer(vec![]);
57    ///     wtr.write_record(&["a", "b", "c"])?;
58    ///     wtr.write_record(&["x", "y", "z"])?;
59    ///
60    ///     let data = String::from_utf8(wtr.into_inner()?)?;
61    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
62    ///     Ok(())
63    /// }
64    /// ```
65    pub fn new() -> WriterBuilder {
66        WriterBuilder::default()
67    }
68
69    /// Build a CSV writer from this configuration that writes data to the
70    /// given file path. The file is truncated if it already exists.
71    ///
72    /// If there was a problem opening the file at the given path, then this
73    /// returns the corresponding error.
74    ///
75    /// # Example
76    ///
77    /// ```no_run
78    /// use std::error::Error;
79    /// use csv::WriterBuilder;
80    ///
81    /// # fn main() { example().unwrap(); }
82    /// fn example() -> Result<(), Box<dyn Error>> {
83    ///     let mut wtr = WriterBuilder::new().from_path("foo.csv")?;
84    ///     wtr.write_record(&["a", "b", "c"])?;
85    ///     wtr.write_record(&["x", "y", "z"])?;
86    ///     wtr.flush()?;
87    ///     Ok(())
88    /// }
89    /// ```
90    pub fn from_path<P: AsRef<Path>>(&self, path: P) -> Result<Writer<File>> {
91        Ok(Writer::new(self, File::create(path)?))
92    }
93
94    /// Build a CSV writer from this configuration that writes data to `wtr`.
95    ///
96    /// Note that the CSV writer is buffered automatically, so you should not
97    /// wrap `wtr` in a buffered writer like `io::BufWriter`.
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// use std::error::Error;
103    /// use csv::WriterBuilder;
104    ///
105    /// # fn main() { example().unwrap(); }
106    /// fn example() -> Result<(), Box<dyn Error>> {
107    ///     let mut wtr = WriterBuilder::new().from_writer(vec![]);
108    ///     wtr.write_record(&["a", "b", "c"])?;
109    ///     wtr.write_record(&["x", "y", "z"])?;
110    ///
111    ///     let data = String::from_utf8(wtr.into_inner()?)?;
112    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
113    ///     Ok(())
114    /// }
115    /// ```
116    pub fn from_writer<W: io::Write>(&self, wtr: W) -> Writer<W> {
117        Writer::new(self, wtr)
118    }
119
120    /// The field delimiter to use when writing CSV.
121    ///
122    /// The default is `b','`.
123    ///
124    /// # Example
125    ///
126    /// ```
127    /// use std::error::Error;
128    /// use csv::WriterBuilder;
129    ///
130    /// # fn main() { example().unwrap(); }
131    /// fn example() -> Result<(), Box<dyn Error>> {
132    ///     let mut wtr = WriterBuilder::new()
133    ///         .delimiter(b';')
134    ///         .from_writer(vec![]);
135    ///     wtr.write_record(&["a", "b", "c"])?;
136    ///     wtr.write_record(&["x", "y", "z"])?;
137    ///
138    ///     let data = String::from_utf8(wtr.into_inner()?)?;
139    ///     assert_eq!(data, "a;b;c\nx;y;z\n");
140    ///     Ok(())
141    /// }
142    /// ```
143    pub fn delimiter(&mut self, delimiter: u8) -> &mut WriterBuilder {
144        self.builder.delimiter(delimiter);
145        self
146    }
147
148    /// Whether to write a header row before writing any other row.
149    ///
150    /// When this is enabled and the `serialize` method is used to write data
151    /// with something that contains field names (i.e., a struct), then a
152    /// header row is written containing the field names before any other row
153    /// is written.
154    ///
155    /// This option has no effect when using other methods to write rows. That
156    /// is, if you don't use `serialize`, then you must write your header row
157    /// explicitly if you want a header row.
158    ///
159    /// This is enabled by default.
160    ///
161    /// # Example: with headers
162    ///
163    /// This shows how the header will be automatically written from the field
164    /// names of a struct.
165    ///
166    /// ```
167    /// use std::error::Error;
168    ///
169    /// use csv::WriterBuilder;
170    ///
171    /// #[derive(serde::Serialize)]
172    /// struct Row<'a> {
173    ///     city: &'a str,
174    ///     country: &'a str,
175    ///     // Serde allows us to name our headers exactly,
176    ///     // even if they don't match our struct field names.
177    ///     #[serde(rename = "popcount")]
178    ///     population: u64,
179    /// }
180    ///
181    /// # fn main() { example().unwrap(); }
182    /// fn example() -> Result<(), Box<dyn Error>> {
183    ///     let mut wtr = WriterBuilder::new().from_writer(vec![]);
184    ///     wtr.serialize(Row {
185    ///         city: "Boston",
186    ///         country: "United States",
187    ///         population: 4628910,
188    ///     })?;
189    ///     wtr.serialize(Row {
190    ///         city: "Concord",
191    ///         country: "United States",
192    ///         population: 42695,
193    ///     })?;
194    ///
195    ///     let data = String::from_utf8(wtr.into_inner()?)?;
196    ///     assert_eq!(data, "\
197    /// city,country,popcount
198    /// Boston,United States,4628910
199    /// Concord,United States,42695
200    /// ");
201    ///     Ok(())
202    /// }
203    /// ```
204    ///
205    /// # Example: without headers
206    ///
207    /// This shows that serializing things that aren't structs (in this case,
208    /// a tuple struct) won't result in a header row being written. This means
209    /// you usually don't need to set `has_headers(false)` unless you
210    /// explicitly want to both write custom headers and serialize structs.
211    ///
212    /// ```
213    /// use std::error::Error;
214    /// use csv::WriterBuilder;
215    ///
216    /// # fn main() { example().unwrap(); }
217    /// fn example() -> Result<(), Box<dyn Error>> {
218    ///     let mut wtr = WriterBuilder::new().from_writer(vec![]);
219    ///     wtr.serialize(("Boston", "United States", 4628910))?;
220    ///     wtr.serialize(("Concord", "United States", 42695))?;
221    ///
222    ///     let data = String::from_utf8(wtr.into_inner()?)?;
223    ///     assert_eq!(data, "\
224    /// Boston,United States,4628910
225    /// Concord,United States,42695
226    /// ");
227    ///     Ok(())
228    /// }
229    /// ```
230    pub fn has_headers(&mut self, yes: bool) -> &mut WriterBuilder {
231        self.has_headers = yes;
232        self
233    }
234
235    /// Whether the number of fields in records is allowed to change or not.
236    ///
237    /// When disabled (which is the default), writing CSV data will return an
238    /// error if a record is written with a number of fields different from the
239    /// number of fields written in a previous record.
240    ///
241    /// When enabled, this error checking is turned off.
242    ///
243    /// # Example: writing flexible records
244    ///
245    /// ```
246    /// use std::error::Error;
247    /// use csv::WriterBuilder;
248    ///
249    /// # fn main() { example().unwrap(); }
250    /// fn example() -> Result<(), Box<dyn Error>> {
251    ///     let mut wtr = WriterBuilder::new()
252    ///         .flexible(true)
253    ///         .from_writer(vec![]);
254    ///     wtr.write_record(&["a", "b"])?;
255    ///     wtr.write_record(&["x", "y", "z"])?;
256    ///
257    ///     let data = String::from_utf8(wtr.into_inner()?)?;
258    ///     assert_eq!(data, "a,b\nx,y,z\n");
259    ///     Ok(())
260    /// }
261    /// ```
262    ///
263    /// # Example: error when `flexible` is disabled
264    ///
265    /// ```
266    /// use std::error::Error;
267    /// use csv::WriterBuilder;
268    ///
269    /// # fn main() { example().unwrap(); }
270    /// fn example() -> Result<(), Box<dyn Error>> {
271    ///     let mut wtr = WriterBuilder::new()
272    ///         .flexible(false)
273    ///         .from_writer(vec![]);
274    ///     wtr.write_record(&["a", "b"])?;
275    ///     let err = wtr.write_record(&["x", "y", "z"]).unwrap_err();
276    ///     match *err.kind() {
277    ///         csv::ErrorKind::UnequalLengths { expected_len, len, .. } => {
278    ///             assert_eq!(expected_len, 2);
279    ///             assert_eq!(len, 3);
280    ///         }
281    ///         ref wrong => {
282    ///             panic!("expected UnequalLengths but got {:?}", wrong);
283    ///         }
284    ///     }
285    ///     Ok(())
286    /// }
287    /// ```
288    pub fn flexible(&mut self, yes: bool) -> &mut WriterBuilder {
289        self.flexible = yes;
290        self
291    }
292
293    /// The record terminator to use when writing CSV.
294    ///
295    /// A record terminator can be any single byte. The default is `\n`.
296    ///
297    /// Note that RFC 4180 specifies that record terminators should be `\r\n`.
298    /// To use `\r\n`, use the special `Terminator::CRLF` value.
299    ///
300    /// # Example: CRLF
301    ///
302    /// This shows how to use RFC 4180 compliant record terminators.
303    ///
304    /// ```
305    /// use std::error::Error;
306    /// use csv::{Terminator, WriterBuilder};
307    ///
308    /// # fn main() { example().unwrap(); }
309    /// fn example() -> Result<(), Box<dyn Error>> {
310    ///     let mut wtr = WriterBuilder::new()
311    ///         .terminator(Terminator::CRLF)
312    ///         .from_writer(vec![]);
313    ///     wtr.write_record(&["a", "b", "c"])?;
314    ///     wtr.write_record(&["x", "y", "z"])?;
315    ///
316    ///     let data = String::from_utf8(wtr.into_inner()?)?;
317    ///     assert_eq!(data, "a,b,c\r\nx,y,z\r\n");
318    ///     Ok(())
319    /// }
320    /// ```
321    pub fn terminator(&mut self, term: Terminator) -> &mut WriterBuilder {
322        self.builder.terminator(term.to_core());
323        self
324    }
325
326    /// The quoting style to use when writing CSV.
327    ///
328    /// By default, this is set to `QuoteStyle::Necessary`, which will only
329    /// use quotes when they are necessary to preserve the integrity of data.
330    ///
331    /// Note that unless the quote style is set to `Never`, an empty field is
332    /// quoted if it is the only field in a record.
333    ///
334    /// # Example: non-numeric quoting
335    ///
336    /// This shows how to quote non-numeric fields only.
337    ///
338    /// ```
339    /// use std::error::Error;
340    /// use csv::{QuoteStyle, WriterBuilder};
341    ///
342    /// # fn main() { example().unwrap(); }
343    /// fn example() -> Result<(), Box<dyn Error>> {
344    ///     let mut wtr = WriterBuilder::new()
345    ///         .quote_style(QuoteStyle::NonNumeric)
346    ///         .from_writer(vec![]);
347    ///     wtr.write_record(&["a", "5", "c"])?;
348    ///     wtr.write_record(&["3.14", "y", "z"])?;
349    ///
350    ///     let data = String::from_utf8(wtr.into_inner()?)?;
351    ///     assert_eq!(data, "\"a\",5,\"c\"\n3.14,\"y\",\"z\"\n");
352    ///     Ok(())
353    /// }
354    /// ```
355    ///
356    /// # Example: never quote
357    ///
358    /// This shows how the CSV writer can be made to never write quotes, even
359    /// if it sacrifices the integrity of the data.
360    ///
361    /// ```
362    /// use std::error::Error;
363    /// use csv::{QuoteStyle, WriterBuilder};
364    ///
365    /// # fn main() { example().unwrap(); }
366    /// fn example() -> Result<(), Box<dyn Error>> {
367    ///     let mut wtr = WriterBuilder::new()
368    ///         .quote_style(QuoteStyle::Never)
369    ///         .from_writer(vec![]);
370    ///     wtr.write_record(&["a", "foo\nbar", "c"])?;
371    ///     wtr.write_record(&["g\"h\"i", "y", "z"])?;
372    ///
373    ///     let data = String::from_utf8(wtr.into_inner()?)?;
374    ///     assert_eq!(data, "a,foo\nbar,c\ng\"h\"i,y,z\n");
375    ///     Ok(())
376    /// }
377    /// ```
378    pub fn quote_style(&mut self, style: QuoteStyle) -> &mut WriterBuilder {
379        self.builder.quote_style(style.to_core());
380        self
381    }
382
383    /// The quote character to use when writing CSV.
384    ///
385    /// The default is `b'"'`.
386    ///
387    /// # Example
388    ///
389    /// ```
390    /// use std::error::Error;
391    /// use csv::WriterBuilder;
392    ///
393    /// # fn main() { example().unwrap(); }
394    /// fn example() -> Result<(), Box<dyn Error>> {
395    ///     let mut wtr = WriterBuilder::new()
396    ///         .quote(b'\'')
397    ///         .from_writer(vec![]);
398    ///     wtr.write_record(&["a", "foo\nbar", "c"])?;
399    ///     wtr.write_record(&["g'h'i", "y\"y\"y", "z"])?;
400    ///
401    ///     let data = String::from_utf8(wtr.into_inner()?)?;
402    ///     assert_eq!(data, "a,'foo\nbar',c\n'g''h''i',y\"y\"y,z\n");
403    ///     Ok(())
404    /// }
405    /// ```
406    pub fn quote(&mut self, quote: u8) -> &mut WriterBuilder {
407        self.builder.quote(quote);
408        self
409    }
410
411    /// Enable double quote escapes.
412    ///
413    /// This is enabled by default, but it may be disabled. When disabled,
414    /// quotes in field data are escaped instead of doubled.
415    ///
416    /// # Example
417    ///
418    /// ```
419    /// use std::error::Error;
420    /// use csv::WriterBuilder;
421    ///
422    /// # fn main() { example().unwrap(); }
423    /// fn example() -> Result<(), Box<dyn Error>> {
424    ///     let mut wtr = WriterBuilder::new()
425    ///         .double_quote(false)
426    ///         .from_writer(vec![]);
427    ///     wtr.write_record(&["a", "foo\"bar", "c"])?;
428    ///     wtr.write_record(&["x", "y", "z"])?;
429    ///
430    ///     let data = String::from_utf8(wtr.into_inner()?)?;
431    ///     assert_eq!(data, "a,\"foo\\\"bar\",c\nx,y,z\n");
432    ///     Ok(())
433    /// }
434    /// ```
435    pub fn double_quote(&mut self, yes: bool) -> &mut WriterBuilder {
436        self.builder.double_quote(yes);
437        self
438    }
439
440    /// The escape character to use when writing CSV.
441    ///
442    /// In some variants of CSV, quotes are escaped using a special escape
443    /// character like `\` (instead of escaping quotes by doubling them).
444    ///
445    /// By default, writing these idiosyncratic escapes is disabled, and is
446    /// only used when `double_quote` is disabled.
447    ///
448    /// # Example
449    ///
450    /// ```
451    /// use std::error::Error;
452    /// use csv::WriterBuilder;
453    ///
454    /// # fn main() { example().unwrap(); }
455    /// fn example() -> Result<(), Box<dyn Error>> {
456    ///     let mut wtr = WriterBuilder::new()
457    ///         .double_quote(false)
458    ///         .escape(b'$')
459    ///         .from_writer(vec![]);
460    ///     wtr.write_record(&["a", "foo\"bar", "c"])?;
461    ///     wtr.write_record(&["x", "y", "z"])?;
462    ///
463    ///     let data = String::from_utf8(wtr.into_inner()?)?;
464    ///     assert_eq!(data, "a,\"foo$\"bar\",c\nx,y,z\n");
465    ///     Ok(())
466    /// }
467    /// ```
468    pub fn escape(&mut self, escape: u8) -> &mut WriterBuilder {
469        self.builder.escape(escape);
470        self
471    }
472
473    /// The comment character that will be used when later reading the file.
474    ///
475    /// If `quote_style` is set to `QuoteStyle::Necessary`, a field will
476    /// be quoted if the comment character is detected anywhere in the field.
477    ///
478    /// The default value is None.
479    ///
480    /// # Example
481    ///
482    /// ```
483    /// use std::error::Error;
484    /// use csv::WriterBuilder;
485    ///
486    /// # fn main() { example().unwrap(); }
487    /// fn example() -> Result<(), Box<dyn Error>> {
488    ///     let mut wtr =
489    ///         WriterBuilder::new().comment(Some(b'#')).from_writer(Vec::new());
490    ///     wtr.write_record(&["# comment", "another"]).unwrap();
491    ///     let buf = wtr.into_inner().unwrap();
492    ///     assert_eq!(String::from_utf8(buf).unwrap(), "\"# comment\",another\n");
493    ///     Ok(())
494    /// }
495    /// ```
496    pub fn comment(&mut self, comment: Option<u8>) -> &mut WriterBuilder {
497        self.builder.comment(comment);
498        self
499    }
500
501    /// Set the capacity (in bytes) of the internal buffer used in the CSV
502    /// writer. This defaults to a reasonable setting.
503    pub fn buffer_capacity(&mut self, capacity: usize) -> &mut WriterBuilder {
504        self.capacity = capacity;
505        self
506    }
507}
508
509/// An already configured CSV writer.
510///
511/// A CSV writer takes as input Rust values and writes those values in a valid
512/// CSV format as output.
513///
514/// While CSV writing is considerably easier than parsing CSV, a proper writer
515/// will do a number of things for you:
516///
517/// 1. Quote fields when necessary.
518/// 2. Check that all records have the same number of fields.
519/// 3. Write records with a single empty field correctly.
520/// 4. Automatically serialize normal Rust types to CSV records. When that
521///    type is a struct, a header row is automatically written corresponding
522///    to the fields of that struct.
523/// 5. Use buffering intelligently and otherwise avoid allocation. (This means
524///    that callers should not do their own buffering.)
525///
526/// All of the above can be configured using a
527/// [`WriterBuilder`](struct.WriterBuilder.html).
528/// However, a `Writer` has a couple of convenience constructors (`from_path`
529/// and `from_writer`) that use the default configuration.
530///
531/// Note that the default configuration of a `Writer` uses `\n` for record
532/// terminators instead of `\r\n` as specified by RFC 4180. Use the
533/// `terminator` method on `WriterBuilder` to set the terminator to `\r\n` if
534/// it's desired.
535#[derive(Debug)]
536pub struct Writer<W: io::Write> {
537    core: CoreWriter,
538    wtr: Option<W>,
539    buf: Buffer,
540    state: WriterState,
541}
542
543#[derive(Debug)]
544struct WriterState {
545    /// Whether the Serde serializer should attempt to write a header row.
546    header: HeaderState,
547    /// Whether inconsistent record lengths are allowed.
548    flexible: bool,
549    /// The number of fields written in the first record. This is compared
550    /// with `fields_written` on all subsequent records to check for
551    /// inconsistent record lengths.
552    first_field_count: Option<u64>,
553    /// The number of fields written in this record. This is used to report
554    /// errors for inconsistent record lengths if `flexible` is disabled.
555    fields_written: u64,
556    /// This is set immediately before flushing the buffer and then unset
557    /// immediately after flushing the buffer. This avoids flushing the buffer
558    /// twice if the inner writer panics.
559    panicked: bool,
560}
561
562/// HeaderState encodes a small state machine for handling header writes.
563#[derive(Debug)]
564enum HeaderState {
565    /// Indicates that we should attempt to write a header.
566    Write,
567    /// Indicates that writing a header was attempted, and a header was written.
568    DidWrite,
569    /// Indicates that writing a header was attempted, but no headers were
570    /// written or the attempt failed.
571    DidNotWrite,
572    /// This state is used when headers are disabled. It cannot transition
573    /// to any other state.
574    None,
575}
576
577/// A simple internal buffer for buffering writes.
578///
579/// We need this because the `csv_core` APIs want to write into a `&mut [u8]`,
580/// which is not available with the `std::io::BufWriter` API.
581#[derive(Debug)]
582struct Buffer {
583    /// The contents of the buffer.
584    buf: Vec<u8>,
585    /// The number of bytes written to the buffer.
586    len: usize,
587}
588
589impl<W: io::Write> Drop for Writer<W> {
590    fn drop(&mut self) {
591        if self.wtr.is_some() && !self.state.panicked {
592            let _ = self.flush();
593        }
594    }
595}
596
597impl Writer<File> {
598    /// Build a CSV writer with a default configuration that writes data to the
599    /// given file path. The file is truncated if it already exists.
600    ///
601    /// If there was a problem opening the file at the given path, then this
602    /// returns the corresponding error.
603    ///
604    /// # Example
605    ///
606    /// ```no_run
607    /// use std::error::Error;
608    /// use csv::Writer;
609    ///
610    /// # fn main() { example().unwrap(); }
611    /// fn example() -> Result<(), Box<dyn Error>> {
612    ///     let mut wtr = Writer::from_path("foo.csv")?;
613    ///     wtr.write_record(&["a", "b", "c"])?;
614    ///     wtr.write_record(&["x", "y", "z"])?;
615    ///     wtr.flush()?;
616    ///     Ok(())
617    /// }
618    /// ```
619    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Writer<File>> {
620        WriterBuilder::new().from_path(path)
621    }
622}
623
624impl<W: io::Write> Writer<W> {
625    fn new(builder: &WriterBuilder, wtr: W) -> Writer<W> {
626        let header_state = if builder.has_headers {
627            HeaderState::Write
628        } else {
629            HeaderState::None
630        };
631        Writer {
632            core: builder.builder.build(),
633            wtr: Some(wtr),
634            buf: Buffer { buf: vec![0; builder.capacity], len: 0 },
635            state: WriterState {
636                header: header_state,
637                flexible: builder.flexible,
638                first_field_count: None,
639                fields_written: 0,
640                panicked: false,
641            },
642        }
643    }
644
645    /// Build a CSV writer with a default configuration that writes data to
646    /// `wtr`.
647    ///
648    /// Note that the CSV writer is buffered automatically, so you should not
649    /// wrap `wtr` in a buffered writer like `io::BufWriter`.
650    ///
651    /// # Example
652    ///
653    /// ```
654    /// use std::error::Error;
655    /// use csv::Writer;
656    ///
657    /// # fn main() { example().unwrap(); }
658    /// fn example() -> Result<(), Box<dyn Error>> {
659    ///     let mut wtr = Writer::from_writer(vec![]);
660    ///     wtr.write_record(&["a", "b", "c"])?;
661    ///     wtr.write_record(&["x", "y", "z"])?;
662    ///
663    ///     let data = String::from_utf8(wtr.into_inner()?)?;
664    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
665    ///     Ok(())
666    /// }
667    /// ```
668    pub fn from_writer(wtr: W) -> Writer<W> {
669        WriterBuilder::new().from_writer(wtr)
670    }
671
672    /// Serialize a single record using Serde.
673    ///
674    /// # Example
675    ///
676    /// This shows how to serialize normal Rust structs as CSV records. The
677    /// fields of the struct are used to write a header row automatically.
678    /// (Writing the header row automatically can be disabled by building the
679    /// CSV writer with a [`WriterBuilder`](struct.WriterBuilder.html) and
680    /// calling the `has_headers` method.)
681    ///
682    /// ```
683    /// use std::error::Error;
684    ///
685    /// use csv::Writer;
686    ///
687    /// #[derive(serde::Serialize)]
688    /// struct Row<'a> {
689    ///     city: &'a str,
690    ///     country: &'a str,
691    ///     // Serde allows us to name our headers exactly,
692    ///     // even if they don't match our struct field names.
693    ///     #[serde(rename = "popcount")]
694    ///     population: u64,
695    /// }
696    ///
697    /// # fn main() { example().unwrap(); }
698    /// fn example() -> Result<(), Box<dyn Error>> {
699    ///     let mut wtr = Writer::from_writer(vec![]);
700    ///     wtr.serialize(Row {
701    ///         city: "Boston",
702    ///         country: "United States",
703    ///         population: 4628910,
704    ///     })?;
705    ///     wtr.serialize(Row {
706    ///         city: "Concord",
707    ///         country: "United States",
708    ///         population: 42695,
709    ///     })?;
710    ///
711    ///     let data = String::from_utf8(wtr.into_inner()?)?;
712    ///     assert_eq!(data, "\
713    /// city,country,popcount
714    /// Boston,United States,4628910
715    /// Concord,United States,42695
716    /// ");
717    ///     Ok(())
718    /// }
719    /// ```
720    ///
721    /// # Rules
722    ///
723    /// The behavior of `serialize` is fairly simple:
724    ///
725    /// 1. Nested containers (tuples, `Vec`s, structs, etc.) are always
726    ///    flattened (depth-first order).
727    ///
728    /// 2. If `has_headers` is `true` and the type contains field names, then
729    ///    a header row is automatically generated.
730    ///
731    /// However, some container types cannot be serialized, and if
732    /// `has_headers` is `true`, there are some additional restrictions on the
733    /// types that can be serialized. See below for details.
734    ///
735    /// For the purpose of this section, Rust types can be divided into three
736    /// categories: scalars, non-struct containers, and structs.
737    ///
738    /// ## Scalars
739    ///
740    /// Single values with no field names are written like the following. Note
741    /// that some of the outputs may be quoted, according to the selected
742    /// quoting style.
743    ///
744    /// | Name | Example Type | Example Value | Output |
745    /// | ---- | ---- | ---- | ---- |
746    /// | boolean | `bool` | `true` | `true` |
747    /// | integers | `i8`, `i16`, `i32`, `i64`, `i128`, `u8`, `u16`, `u32`, `u64`, `u128` | `5` | `5` |
748    /// | floats | `f32`, `f64` | `3.14` | `3.14` |
749    /// | character | `char` | `'☃'` | `☃` |
750    /// | string | `&str` | `"hi"` | `hi` |
751    /// | bytes | `&[u8]` | `b"hi"[..]` | `hi` |
752    /// | option | `Option` | `None` | *empty* |
753    /// | option |          | `Some(5)` | `5` |
754    /// | unit | `()` | `()` | *empty* |
755    /// | unit struct | `struct Foo;` | `Foo` | `Foo` |
756    /// | unit enum variant | `enum E { A, B }` | `E::A` | `A` |
757    /// | newtype struct | `struct Foo(u8);` | `Foo(5)` | `5` |
758    /// | newtype enum variant | `enum E { A(u8) }` | `E::A(5)` | `5` |
759    ///
760    /// Note that this table includes simple structs and enums. For example, to
761    /// serialize a field from either an integer or a float type, one can do
762    /// this:
763    ///
764    /// ```
765    /// use std::error::Error;
766    ///
767    /// use csv::Writer;
768    ///
769    /// #[derive(serde::Serialize)]
770    /// struct Row {
771    ///     label: String,
772    ///     value: Value,
773    /// }
774    ///
775    /// #[derive(serde::Serialize)]
776    /// enum Value {
777    ///     Integer(i64),
778    ///     Float(f64),
779    /// }
780    ///
781    /// # fn main() { example().unwrap(); }
782    /// fn example() -> Result<(), Box<dyn Error>> {
783    ///     let mut wtr = Writer::from_writer(vec![]);
784    ///     wtr.serialize(Row {
785    ///         label: "foo".to_string(),
786    ///         value: Value::Integer(3),
787    ///     })?;
788    ///     wtr.serialize(Row {
789    ///         label: "bar".to_string(),
790    ///         value: Value::Float(3.14),
791    ///     })?;
792    ///
793    ///     let data = String::from_utf8(wtr.into_inner()?)?;
794    ///     assert_eq!(data, "\
795    /// label,value
796    /// foo,3
797    /// bar,3.14
798    /// ");
799    ///     Ok(())
800    /// }
801    /// ```
802    ///
803    /// ## Non-Struct Containers
804    ///
805    /// Nested containers are flattened to their scalar components, with the
806    /// exception of a few types that are not allowed:
807    ///
808    /// | Name | Example Type | Example Value | Output |
809    /// | ---- | ---- | ---- | ---- |
810    /// | sequence | `Vec<u8>` | `vec![1, 2, 3]` | `1,2,3` |
811    /// | tuple | `(u8, bool)` | `(5, true)` | `5,true` |
812    /// | tuple struct | `Foo(u8, bool)` | `Foo(5, true)` | `5,true` |
813    /// | tuple enum variant | `enum E { A(u8, bool) }` | `E::A(5, true)` | *error* |
814    /// | struct enum variant | `enum E { V { a: u8, b: bool } }` | `E::V { a: 5, b: true }` | *error* |
815    /// | map | `BTreeMap<K, V>` | `BTreeMap::new()` | *error* |
816    ///
817    /// ## Structs
818    ///
819    /// Like the other containers, structs are flattened to their scalar
820    /// components:
821    ///
822    /// | Name | Example Type | Example Value | Output |
823    /// | ---- | ---- | ---- | ---- |
824    /// | struct | `struct Foo { a: u8, b: bool }` | `Foo { a: 5, b: true }` | `5,true` |
825    ///
826    /// If `has_headers` is `false`, then there are no additional restrictions;
827    /// types can be nested arbitrarily. For example:
828    ///
829    /// ```
830    /// use std::error::Error;
831    ///
832    /// use csv::WriterBuilder;
833    ///
834    /// #[derive(serde::Serialize)]
835    /// struct Row {
836    ///     label: String,
837    ///     values: Vec<f64>,
838    /// }
839    ///
840    /// # fn main() { example().unwrap(); }
841    /// fn example() -> Result<(), Box<dyn Error>> {
842    ///     let mut wtr = WriterBuilder::new()
843    ///         .has_headers(false)
844    ///         .from_writer(vec![]);
845    ///     wtr.serialize(Row {
846    ///         label: "foo".to_string(),
847    ///         values: vec![1.1234, 2.5678, 3.14],
848    ///     })?;
849    ///
850    ///     let data = String::from_utf8(wtr.into_inner()?)?;
851    ///     assert_eq!(data, "\
852    /// foo,1.1234,2.5678,3.14
853    /// ");
854    ///     Ok(())
855    /// }
856    /// ```
857    ///
858    /// However, if `has_headers` were enabled in the above example, then
859    /// serialization would return an error. Specifically, when `has_headers` is
860    /// `true`, there are two restrictions:
861    ///
862    /// 1. Named field values in structs must be scalars.
863    ///
864    /// 2. All scalars must be named field values in structs.
865    ///
866    /// Other than these two restrictions, types can be nested arbitrarily.
867    /// Here are a few examples:
868    ///
869    /// | Value | Header | Record |
870    /// | ---- | ---- | ---- |
871    /// | `(Foo { x: 5, y: 6 }, Bar { z: true })` | `x,y,z` | `5,6,true` |
872    /// | `vec![Foo { x: 5, y: 6 }, Foo { x: 7, y: 8 }]` | `x,y,x,y` | `5,6,7,8` |
873    /// | `(Foo { x: 5, y: 6 }, vec![Bar { z: Baz(true) }])` | `x,y,z` | `5,6,true` |
874    /// | `Foo { x: 5, y: (6, 7) }` | *error: restriction 1* | `5,6,7` |
875    /// | `(5, Foo { x: 6, y: 7 }` | *error: restriction 2* | `5,6,7` |
876    /// | `(Foo { x: 5, y: 6 }, true)` | *error: restriction 2* | `5,6,true` |
877    pub fn serialize<S: Serialize>(&mut self, record: S) -> Result<()> {
878        if let HeaderState::Write = self.state.header {
879            let wrote_header = serialize_header(self, &record)?;
880            if wrote_header {
881                self.write_terminator()?;
882                self.state.header = HeaderState::DidWrite;
883            } else {
884                self.state.header = HeaderState::DidNotWrite;
885            };
886        }
887        serialize(self, &record)?;
888        self.write_terminator()?;
889        Ok(())
890    }
891
892    /// Write a single record.
893    ///
894    /// This method accepts something that can be turned into an iterator that
895    /// yields elements that can be represented by a `&[u8]`.
896    ///
897    /// This may be called with an empty iterator, which will cause a record
898    /// terminator to be written. If no fields had been written, then a single
899    /// empty field is written before the terminator.
900    ///
901    /// # Example
902    ///
903    /// ```
904    /// use std::error::Error;
905    /// use csv::Writer;
906    ///
907    /// # fn main() { example().unwrap(); }
908    /// fn example() -> Result<(), Box<dyn Error>> {
909    ///     let mut wtr = Writer::from_writer(vec![]);
910    ///     wtr.write_record(&["a", "b", "c"])?;
911    ///     wtr.write_record(&["x", "y", "z"])?;
912    ///
913    ///     let data = String::from_utf8(wtr.into_inner()?)?;
914    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
915    ///     Ok(())
916    /// }
917    /// ```
918    pub fn write_record<I, T>(&mut self, record: I) -> Result<()>
919    where
920        I: IntoIterator<Item = T>,
921        T: AsRef<[u8]>,
922    {
923        for field in record.into_iter() {
924            self.write_field_impl(field)?;
925        }
926        self.write_terminator()
927    }
928
929    /// Write a single `ByteRecord`.
930    ///
931    /// This method accepts a borrowed `ByteRecord` and writes its contents
932    /// to the underlying writer.
933    ///
934    /// This is similar to `write_record` except that it specifically requires
935    /// a `ByteRecord`. This permits the writer to possibly write the record
936    /// more quickly than the more generic `write_record`.
937    ///
938    /// This may be called with an empty record, which will cause a record
939    /// terminator to be written. If no fields had been written, then a single
940    /// empty field is written before the terminator.
941    ///
942    /// # Example
943    ///
944    /// ```
945    /// use std::error::Error;
946    /// use csv::{ByteRecord, Writer};
947    ///
948    /// # fn main() { example().unwrap(); }
949    /// fn example() -> Result<(), Box<dyn Error>> {
950    ///     let mut wtr = Writer::from_writer(vec![]);
951    ///     wtr.write_byte_record(&ByteRecord::from(&["a", "b", "c"][..]))?;
952    ///     wtr.write_byte_record(&ByteRecord::from(&["x", "y", "z"][..]))?;
953    ///
954    ///     let data = String::from_utf8(wtr.into_inner()?)?;
955    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
956    ///     Ok(())
957    /// }
958    /// ```
959    #[inline(never)]
960    pub fn write_byte_record(&mut self, record: &ByteRecord) -> Result<()> {
961        if record.as_slice().is_empty() {
962            return self.write_record(record);
963        }
964        // The idea here is to find a fast path for shuffling our record into
965        // our buffer as quickly as possible. We do this because the underlying
966        // "core" CSV writer does a lot of book-keeping to maintain its state
967        // oriented API.
968        //
969        // The fast path occurs when we know our record will fit in whatever
970        // space we have left in our buffer. We can actually quickly compute
971        // the upper bound on the space required:
972        let upper_bound =
973            // The data itself plus the worst case: every byte is a quote.
974            (2 * record.as_slice().len())
975            // The number of field delimiters.
976            + (record.len().saturating_sub(1))
977            // The maximum number of quotes inserted around each field.
978            + (2 * record.len())
979            // The maximum number of bytes for the terminator.
980            + 2;
981        if self.buf.writable().len() < upper_bound {
982            return self.write_record(record);
983        }
984        let mut first = true;
985        for field in record.iter() {
986            if !first {
987                self.buf.writable()[0] = self.core.get_delimiter();
988                self.buf.written(1);
989            }
990            first = false;
991
992            if !self.core.should_quote(field) {
993                self.buf.writable()[..field.len()].copy_from_slice(field);
994                self.buf.written(field.len());
995            } else {
996                self.buf.writable()[0] = self.core.get_quote();
997                self.buf.written(1);
998                let (res, nin, nout) = csv_core::quote(
999                    field,
1000                    self.buf.writable(),
1001                    self.core.get_quote(),
1002                    self.core.get_escape(),
1003                    self.core.get_double_quote(),
1004                );
1005                debug_assert!(res == WriteResult::InputEmpty);
1006                debug_assert!(nin == field.len());
1007                self.buf.written(nout);
1008                self.buf.writable()[0] = self.core.get_quote();
1009                self.buf.written(1);
1010            }
1011        }
1012        self.state.fields_written = record.len() as u64;
1013        self.write_terminator_into_buffer()
1014    }
1015
1016    /// Write a single field.
1017    ///
1018    /// One should prefer using `write_record` over this method. It is provided
1019    /// for cases where writing a field at a time is more convenient than
1020    /// writing a record at a time.
1021    ///
1022    /// Note that if this API is used, `write_record` should be called with an
1023    /// empty iterator to write a record terminator.
1024    ///
1025    /// # Example
1026    ///
1027    /// ```
1028    /// use std::error::Error;
1029    /// use csv::Writer;
1030    ///
1031    /// # fn main() { example().unwrap(); }
1032    /// fn example() -> Result<(), Box<dyn Error>> {
1033    ///     let mut wtr = Writer::from_writer(vec![]);
1034    ///     wtr.write_field("a")?;
1035    ///     wtr.write_field("b")?;
1036    ///     wtr.write_field("c")?;
1037    ///     wtr.write_record(None::<&[u8]>)?;
1038    ///     wtr.write_field("x")?;
1039    ///     wtr.write_field("y")?;
1040    ///     wtr.write_field("z")?;
1041    ///     wtr.write_record(None::<&[u8]>)?;
1042    ///
1043    ///     let data = String::from_utf8(wtr.into_inner()?)?;
1044    ///     assert_eq!(data, "a,b,c\nx,y,z\n");
1045    ///     Ok(())
1046    /// }
1047    /// ```
1048    pub fn write_field<T: AsRef<[u8]>>(&mut self, field: T) -> Result<()> {
1049        self.write_field_impl(field)
1050    }
1051
1052    /// Implementation of write_field.
1053    ///
1054    /// This is a separate method so we can force the compiler to inline it
1055    /// into write_record.
1056    #[inline(always)]
1057    fn write_field_impl<T: AsRef<[u8]>>(&mut self, field: T) -> Result<()> {
1058        if self.state.fields_written > 0 {
1059            self.write_delimiter()?;
1060        }
1061        let mut field = field.as_ref();
1062        loop {
1063            let (res, nin, nout) = self.core.field(field, self.buf.writable());
1064            field = &field[nin..];
1065            self.buf.written(nout);
1066            match res {
1067                WriteResult::InputEmpty => {
1068                    self.state.fields_written += 1;
1069                    return Ok(());
1070                }
1071                WriteResult::OutputFull => self.flush_buf()?,
1072            }
1073        }
1074    }
1075
1076    /// Flush the contents of the internal buffer to the underlying writer.
1077    ///
1078    /// If there was a problem writing to the underlying writer, then an error
1079    /// is returned.
1080    ///
1081    /// Note that this also flushes the underlying writer.
1082    pub fn flush(&mut self) -> io::Result<()> {
1083        self.flush_buf()?;
1084        self.wtr.as_mut().unwrap().flush()?;
1085        Ok(())
1086    }
1087
1088    /// Flush the contents of the internal buffer to the underlying writer,
1089    /// without flushing the underlying writer.
1090    fn flush_buf(&mut self) -> io::Result<()> {
1091        self.state.panicked = true;
1092        let result = self.wtr.as_mut().unwrap().write_all(self.buf.readable());
1093        self.state.panicked = false;
1094        result?;
1095        self.buf.clear();
1096        Ok(())
1097    }
1098
1099    /// Return a reference to the underlying writer.
1100    pub fn get_ref(&self) -> &W {
1101        self.wtr.as_ref().unwrap()
1102    }
1103
1104    /// Flush the contents of the internal buffer and return the underlying
1105    /// writer.
1106    pub fn into_inner(
1107        mut self,
1108    ) -> result::Result<W, IntoInnerError<Writer<W>>> {
1109        match self.flush() {
1110            Ok(()) => Ok(self.wtr.take().unwrap()),
1111            Err(err) => Err(IntoInnerError::new(self, err)),
1112        }
1113    }
1114
1115    /// Write a CSV delimiter.
1116    fn write_delimiter(&mut self) -> Result<()> {
1117        loop {
1118            let (res, nout) = self.core.delimiter(self.buf.writable());
1119            self.buf.written(nout);
1120            match res {
1121                WriteResult::InputEmpty => return Ok(()),
1122                WriteResult::OutputFull => self.flush_buf()?,
1123            }
1124        }
1125    }
1126
1127    /// Write a CSV terminator.
1128    fn write_terminator(&mut self) -> Result<()> {
1129        self.check_field_count()?;
1130        loop {
1131            let (res, nout) = self.core.terminator(self.buf.writable());
1132            self.buf.written(nout);
1133            match res {
1134                WriteResult::InputEmpty => {
1135                    self.state.fields_written = 0;
1136                    return Ok(());
1137                }
1138                WriteResult::OutputFull => self.flush_buf()?,
1139            }
1140        }
1141    }
1142
1143    /// Write a CSV terminator that is guaranteed to fit into the current
1144    /// buffer.
1145    #[inline(never)]
1146    fn write_terminator_into_buffer(&mut self) -> Result<()> {
1147        self.check_field_count()?;
1148        match self.core.get_terminator() {
1149            csv_core::Terminator::CRLF => {
1150                self.buf.writable()[0] = b'\r';
1151                self.buf.writable()[1] = b'\n';
1152                self.buf.written(2);
1153            }
1154            csv_core::Terminator::Any(b) => {
1155                self.buf.writable()[0] = b;
1156                self.buf.written(1);
1157            }
1158            _ => unreachable!(),
1159        }
1160        self.state.fields_written = 0;
1161        Ok(())
1162    }
1163
1164    fn check_field_count(&mut self) -> Result<()> {
1165        if !self.state.flexible {
1166            match self.state.first_field_count {
1167                None => {
1168                    self.state.first_field_count =
1169                        Some(self.state.fields_written);
1170                }
1171                Some(expected) if expected != self.state.fields_written => {
1172                    return Err(Error::new(ErrorKind::UnequalLengths {
1173                        pos: None,
1174                        expected_len: expected,
1175                        len: self.state.fields_written,
1176                    }))
1177                }
1178                Some(_) => {}
1179            }
1180        }
1181        Ok(())
1182    }
1183}
1184
1185impl Buffer {
1186    /// Returns a slice of the buffer's current contents.
1187    ///
1188    /// The slice returned may be empty.
1189    #[inline]
1190    fn readable(&self) -> &[u8] {
1191        &self.buf[..self.len]
1192    }
1193
1194    /// Returns a mutable slice of the remaining space in this buffer.
1195    ///
1196    /// The slice returned may be empty.
1197    #[inline]
1198    fn writable(&mut self) -> &mut [u8] {
1199        &mut self.buf[self.len..]
1200    }
1201
1202    /// Indicates that `n` bytes have been written to this buffer.
1203    #[inline]
1204    fn written(&mut self, n: usize) {
1205        self.len += n;
1206    }
1207
1208    /// Clear the buffer.
1209    #[inline]
1210    fn clear(&mut self) {
1211        self.len = 0;
1212    }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217    use std::io::{self, Write};
1218
1219    use serde::{serde_if_integer128, Serialize};
1220
1221    use crate::{
1222        byte_record::ByteRecord, error::ErrorKind, string_record::StringRecord,
1223    };
1224
1225    use super::{Writer, WriterBuilder};
1226
1227    fn wtr_as_string(wtr: Writer<Vec<u8>>) -> String {
1228        String::from_utf8(wtr.into_inner().unwrap()).unwrap()
1229    }
1230
1231    #[test]
1232    fn one_record() {
1233        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1234        wtr.write_record(&["a", "b", "c"]).unwrap();
1235
1236        assert_eq!(wtr_as_string(wtr), "a,b,c\n");
1237    }
1238
1239    #[test]
1240    fn one_string_record() {
1241        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1242        wtr.write_record(&StringRecord::from(vec!["a", "b", "c"])).unwrap();
1243
1244        assert_eq!(wtr_as_string(wtr), "a,b,c\n");
1245    }
1246
1247    #[test]
1248    fn one_byte_record() {
1249        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1250        wtr.write_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1251
1252        assert_eq!(wtr_as_string(wtr), "a,b,c\n");
1253    }
1254
1255    #[test]
1256    fn raw_one_byte_record() {
1257        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1258        wtr.write_byte_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1259
1260        assert_eq!(wtr_as_string(wtr), "a,b,c\n");
1261    }
1262
1263    #[test]
1264    fn one_empty_record() {
1265        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1266        wtr.write_record(&[""]).unwrap();
1267
1268        assert_eq!(wtr_as_string(wtr), "\"\"\n");
1269    }
1270
1271    #[test]
1272    fn raw_one_empty_record() {
1273        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1274        wtr.write_byte_record(&ByteRecord::from(vec![""])).unwrap();
1275
1276        assert_eq!(wtr_as_string(wtr), "\"\"\n");
1277    }
1278
1279    #[test]
1280    fn two_empty_records() {
1281        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1282        wtr.write_record(&[""]).unwrap();
1283        wtr.write_record(&[""]).unwrap();
1284
1285        assert_eq!(wtr_as_string(wtr), "\"\"\n\"\"\n");
1286    }
1287
1288    #[test]
1289    fn raw_two_empty_records() {
1290        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1291        wtr.write_byte_record(&ByteRecord::from(vec![""])).unwrap();
1292        wtr.write_byte_record(&ByteRecord::from(vec![""])).unwrap();
1293
1294        assert_eq!(wtr_as_string(wtr), "\"\"\n\"\"\n");
1295    }
1296
1297    #[test]
1298    fn unequal_records_bad() {
1299        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1300        wtr.write_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1301        let err = wtr.write_record(&ByteRecord::from(vec!["a"])).unwrap_err();
1302        match *err.kind() {
1303            ErrorKind::UnequalLengths { ref pos, expected_len, len } => {
1304                assert!(pos.is_none());
1305                assert_eq!(expected_len, 3);
1306                assert_eq!(len, 1);
1307            }
1308            ref x => {
1309                panic!("expected UnequalLengths error, but got '{:?}'", x);
1310            }
1311        }
1312    }
1313
1314    #[test]
1315    fn raw_unequal_records_bad() {
1316        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1317        wtr.write_byte_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1318        let err =
1319            wtr.write_byte_record(&ByteRecord::from(vec!["a"])).unwrap_err();
1320        match *err.kind() {
1321            ErrorKind::UnequalLengths { ref pos, expected_len, len } => {
1322                assert!(pos.is_none());
1323                assert_eq!(expected_len, 3);
1324                assert_eq!(len, 1);
1325            }
1326            ref x => {
1327                panic!("expected UnequalLengths error, but got '{:?}'", x);
1328            }
1329        }
1330    }
1331
1332    #[test]
1333    fn unequal_records_ok() {
1334        let mut wtr = WriterBuilder::new().flexible(true).from_writer(vec![]);
1335        wtr.write_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1336        wtr.write_record(&ByteRecord::from(vec!["a"])).unwrap();
1337        assert_eq!(wtr_as_string(wtr), "a,b,c\na\n");
1338    }
1339
1340    #[test]
1341    fn raw_unequal_records_ok() {
1342        let mut wtr = WriterBuilder::new().flexible(true).from_writer(vec![]);
1343        wtr.write_byte_record(&ByteRecord::from(vec!["a", "b", "c"])).unwrap();
1344        wtr.write_byte_record(&ByteRecord::from(vec!["a"])).unwrap();
1345        assert_eq!(wtr_as_string(wtr), "a,b,c\na\n");
1346    }
1347
1348    #[test]
1349    fn full_buffer_should_not_flush_underlying() {
1350        struct MarkWriteAndFlush(Vec<u8>);
1351
1352        impl MarkWriteAndFlush {
1353            fn to_str(self) -> String {
1354                String::from_utf8(self.0).unwrap()
1355            }
1356        }
1357
1358        impl Write for MarkWriteAndFlush {
1359            fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1360                self.0.write(b">")?;
1361                let written = self.0.write(data)?;
1362                self.0.write(b"<")?;
1363
1364                Ok(written)
1365            }
1366
1367            fn flush(&mut self) -> io::Result<()> {
1368                self.0.write(b"!")?;
1369                Ok(())
1370            }
1371        }
1372
1373        let underlying = MarkWriteAndFlush(vec![]);
1374        let mut wtr =
1375            WriterBuilder::new().buffer_capacity(4).from_writer(underlying);
1376
1377        wtr.write_byte_record(&ByteRecord::from(vec!["a", "b"])).unwrap();
1378        wtr.write_byte_record(&ByteRecord::from(vec!["c", "d"])).unwrap();
1379        wtr.flush().unwrap();
1380        wtr.write_byte_record(&ByteRecord::from(vec!["e", "f"])).unwrap();
1381
1382        let got = wtr.into_inner().unwrap().to_str();
1383
1384        // As the buffer size is 4 we should write each record separately, and
1385        // flush when explicitly called and implictly in into_inner.
1386        assert_eq!(got, ">a,b\n<>c,d\n<!>e,f\n<!");
1387    }
1388
1389    #[test]
1390    fn serialize_with_headers() {
1391        #[derive(Serialize)]
1392        struct Row {
1393            foo: i32,
1394            bar: f64,
1395            baz: bool,
1396        }
1397
1398        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1399        wtr.serialize(Row { foo: 42, bar: 42.5, baz: true }).unwrap();
1400        assert_eq!(wtr_as_string(wtr), "foo,bar,baz\n42,42.5,true\n");
1401    }
1402
1403    #[test]
1404    fn serialize_no_headers() {
1405        #[derive(Serialize)]
1406        struct Row {
1407            foo: i32,
1408            bar: f64,
1409            baz: bool,
1410        }
1411
1412        let mut wtr =
1413            WriterBuilder::new().has_headers(false).from_writer(vec![]);
1414        wtr.serialize(Row { foo: 42, bar: 42.5, baz: true }).unwrap();
1415        assert_eq!(wtr_as_string(wtr), "42,42.5,true\n");
1416    }
1417
1418    serde_if_integer128! {
1419        #[test]
1420        fn serialize_no_headers_128() {
1421            #[derive(Serialize)]
1422            struct Row {
1423                foo: i128,
1424                bar: f64,
1425                baz: bool,
1426            }
1427
1428            let mut wtr =
1429                WriterBuilder::new().has_headers(false).from_writer(vec![]);
1430            wtr.serialize(Row {
1431                foo: 9_223_372_036_854_775_808,
1432                bar: 42.5,
1433                baz: true,
1434            }).unwrap();
1435            assert_eq!(wtr_as_string(wtr), "9223372036854775808,42.5,true\n");
1436        }
1437    }
1438
1439    #[test]
1440    fn serialize_tuple() {
1441        let mut wtr = WriterBuilder::new().from_writer(vec![]);
1442        wtr.serialize((true, 1.3, "hi")).unwrap();
1443        assert_eq!(wtr_as_string(wtr), "true,1.3,hi\n");
1444    }
1445
1446    #[test]
1447    fn comment_char_is_automatically_quoted() {
1448        let mut wtr =
1449            WriterBuilder::new().comment(Some(b'#')).from_writer(Vec::new());
1450        wtr.write_record(&["# comment", "another"]).unwrap();
1451        let buf = wtr.into_inner().unwrap();
1452        assert_eq!(String::from_utf8(buf).unwrap(), "\"# comment\",another\n");
1453    }
1454}