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}