1 use std::{fs::File, io, path::Path, result}; 2 3 use { 4 csv_core::{ 5 self, WriteResult, Writer as CoreWriter, 6 WriterBuilder as CoreWriterBuilder, 7 }, 8 serde::Serialize, 9 }; 10 11 use 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)] 24 pub struct WriterBuilder { 25 builder: CoreWriterBuilder, 26 capacity: usize, 27 flexible: bool, 28 has_headers: bool, 29 } 30 31 impl Default for WriterBuilder { default() -> WriterBuilder32 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 42 impl 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 /// ``` new() -> WriterBuilder65 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 /// ``` from_path<P: AsRef<Path>>(&self, path: P) -> Result<Writer<File>>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 /// ``` from_writer<W: io::Write>(&self, wtr: W) -> Writer<W>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 /// ``` delimiter(&mut self, delimiter: u8) -> &mut WriterBuilder143 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 /// ``` has_headers(&mut self, yes: bool) -> &mut WriterBuilder230 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 /// ``` flexible(&mut self, yes: bool) -> &mut WriterBuilder288 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 /// ``` terminator(&mut self, term: Terminator) -> &mut WriterBuilder321 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 /// ``` quote_style(&mut self, style: QuoteStyle) -> &mut WriterBuilder378 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 /// ``` quote(&mut self, quote: u8) -> &mut WriterBuilder406 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 /// ``` double_quote(&mut self, yes: bool) -> &mut WriterBuilder435 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 /// ``` escape(&mut self, escape: u8) -> &mut WriterBuilder468 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 /// ``` comment(&mut self, comment: Option<u8>) -> &mut WriterBuilder496 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. buffer_capacity(&mut self, capacity: usize) -> &mut WriterBuilder503 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)] 536 pub struct Writer<W: io::Write> { 537 core: CoreWriter, 538 wtr: Option<W>, 539 buf: Buffer, 540 state: WriterState, 541 } 542 543 #[derive(Debug)] 544 struct 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)] 564 enum 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)] 582 struct 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 589 impl<W: io::Write> Drop for Writer<W> { drop(&mut self)590 fn drop(&mut self) { 591 if self.wtr.is_some() && !self.state.panicked { 592 let _ = self.flush(); 593 } 594 } 595 } 596 597 impl 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 /// ``` from_path<P: AsRef<Path>>(path: P) -> Result<Writer<File>>619 pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Writer<File>> { 620 WriterBuilder::new().from_path(path) 621 } 622 } 623 624 impl<W: io::Write> Writer<W> { new(builder: &WriterBuilder, wtr: W) -> 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 /// ``` from_writer(wtr: W) -> Writer<W>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` | serialize<S: Serialize>(&mut self, record: S) -> Result<()>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 /// ``` write_record<I, T>(&mut self, record: I) -> Result<()> where I: IntoIterator<Item = T>, T: AsRef<[u8]>,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)] write_byte_record(&mut self, record: &ByteRecord) -> Result<()>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 /// ``` write_field<T: AsRef<[u8]>>(&mut self, field: T) -> Result<()>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)] write_field_impl<T: AsRef<[u8]>>(&mut self, field: T) -> Result<()>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. flush(&mut self) -> io::Result<()>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. flush_buf(&mut self) -> io::Result<()>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. get_ref(&self) -> &W1100 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. into_inner( mut self, ) -> result::Result<W, IntoInnerError<Writer<W>>>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. write_delimiter(&mut self) -> Result<()>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. write_terminator(&mut self) -> Result<()>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)] write_terminator_into_buffer(&mut self) -> Result<()>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 check_field_count(&mut self) -> Result<()>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 1185 impl Buffer { 1186 /// Returns a slice of the buffer's current contents. 1187 /// 1188 /// The slice returned may be empty. 1189 #[inline] readable(&self) -> &[u8]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] writable(&mut self) -> &mut [u8]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] written(&mut self, n: usize)1204 fn written(&mut self, n: usize) { 1205 self.len += n; 1206 } 1207 1208 /// Clear the buffer. 1209 #[inline] clear(&mut self)1210 fn clear(&mut self) { 1211 self.len = 0; 1212 } 1213 } 1214 1215 #[cfg(test)] 1216 mod 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 wtr_as_string(wtr: Writer<Vec<u8>>) -> String1227 fn wtr_as_string(wtr: Writer<Vec<u8>>) -> String { 1228 String::from_utf8(wtr.into_inner().unwrap()).unwrap() 1229 } 1230 1231 #[test] one_record()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] one_string_record()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] one_byte_record()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] raw_one_byte_record()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] one_empty_record()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] raw_one_empty_record()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] two_empty_records()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] raw_two_empty_records()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] unequal_records_bad()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] raw_unequal_records_bad()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] unequal_records_ok()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] raw_unequal_records_ok()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] full_buffer_should_not_flush_underlying()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] serialize_with_headers()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] serialize_no_headers()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] serialize_tuple()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] comment_char_is_automatically_quoted()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 } 1455