| /*! |
| `csv-core` provides a fast CSV reader and writer for use in a `no_std` context. |
| |
| This crate will never use the standard library. `no_std` support is therefore |
| enabled by default. |
| |
| If you're looking for more ergonomic CSV parsing routines, please use the |
| [`csv`](https://docs.rs/csv) crate. |
| |
| # Overview |
| |
| This crate has two primary APIs. The `Reader` API provides a CSV parser, and |
| the `Writer` API provides a CSV writer. |
| |
| # Example: reading CSV |
| |
| This example shows how to count the number of fields and records in CSV data. |
| |
| ``` |
| use csv_core::{Reader, ReadFieldResult}; |
| |
| let data = " |
| foo,bar,baz |
| a,b,c |
| xxx,yyy,zzz |
| "; |
| |
| let mut rdr = Reader::new(); |
| let mut bytes = data.as_bytes(); |
| let mut count_fields = 0; |
| let mut count_records = 0; |
| loop { |
| // We skip handling the output since we don't need it for counting. |
| let (result, nin, _) = rdr.read_field(bytes, &mut [0; 1024]); |
| bytes = &bytes[nin..]; |
| match result { |
| ReadFieldResult::InputEmpty => {}, |
| ReadFieldResult::OutputFull => panic!("field too large"), |
| ReadFieldResult::Field { record_end } => { |
| count_fields += 1; |
| if record_end { |
| count_records += 1; |
| } |
| } |
| ReadFieldResult::End => break, |
| } |
| } |
| assert_eq!(3, count_records); |
| assert_eq!(9, count_fields); |
| ``` |
| |
| # Example: writing CSV |
| |
| This example shows how to use the `Writer` API to write valid CSV data. Proper |
| quoting is handled automatically. |
| |
| ``` |
| use csv_core::Writer; |
| |
| // This is where we'll write out CSV data. |
| let mut out = &mut [0; 1024]; |
| // The number of bytes we've written to `out`. |
| let mut nout = 0; |
| // Create a CSV writer with a default configuration. |
| let mut wtr = Writer::new(); |
| |
| // Write a single field. Note that we ignore the `WriteResult` and the number |
| // of input bytes consumed since we're doing this by hand. |
| let (_, _, n) = wtr.field(&b"foo"[..], &mut out[nout..]); |
| nout += n; |
| |
| // Write a delimiter and then another field that requires quotes. |
| let (_, n) = wtr.delimiter(&mut out[nout..]); |
| nout += n; |
| let (_, _, n) = wtr.field(&b"bar,baz"[..], &mut out[nout..]); |
| nout += n; |
| let (_, n) = wtr.terminator(&mut out[nout..]); |
| nout += n; |
| |
| // Now write another record. |
| let (_, _, n) = wtr.field(&b"a \"b\" c"[..], &mut out[nout..]); |
| nout += n; |
| let (_, n) = wtr.delimiter(&mut out[nout..]); |
| nout += n; |
| let (_, _, n) = wtr.field(&b"quux"[..], &mut out[nout..]); |
| nout += n; |
| |
| // We must always call finish once done writing. |
| // This ensures that any closing quotes are written. |
| let (_, n) = wtr.finish(&mut out[nout..]); |
| nout += n; |
| |
| assert_eq!(&out[..nout], &b"\ |
| foo,\"bar,baz\" |
| \"a \"\"b\"\" c\",quux"[..]); |
| ``` |
| */ |
| |
| #![deny(missing_docs)] |
| #![no_std] |
| |
| pub use crate::reader::{ |
| ReadFieldNoCopyResult, ReadFieldResult, ReadRecordNoCopyResult, |
| ReadRecordResult, Reader, ReaderBuilder, |
| }; |
| pub use crate::writer::{ |
| is_non_numeric, quote, WriteResult, Writer, WriterBuilder, |
| }; |
| |
| mod reader; |
| mod writer; |
| |
| /// A record terminator. |
| /// |
| /// Use this to specify the record terminator while parsing CSV. The default is |
| /// CRLF, which treats `\r`, `\n` or `\r\n` as a single record terminator. |
| #[derive(Clone, Copy, Debug)] |
| pub enum Terminator { |
| /// Parses `\r`, `\n` or `\r\n` as a single record terminator. |
| CRLF, |
| /// Parses the byte given as a record terminator. |
| Any(u8), |
| /// Hints that destructuring should not be exhaustive. |
| /// |
| /// This enum may grow additional variants, so this makes sure clients |
| /// don't count on exhaustive matching. (Otherwise, adding a new variant |
| /// could break existing code.) |
| #[doc(hidden)] |
| __Nonexhaustive, |
| } |
| |
| impl Terminator { |
| /// Checks whether the terminator is set to CRLF. |
| fn is_crlf(&self) -> bool { |
| match *self { |
| Terminator::CRLF => true, |
| Terminator::Any(_) => false, |
| _ => unreachable!(), |
| } |
| } |
| |
| fn equals(&self, other: u8) -> bool { |
| match *self { |
| Terminator::CRLF => other == b'\r' || other == b'\n', |
| Terminator::Any(b) => other == b, |
| _ => unreachable!(), |
| } |
| } |
| } |
| |
| impl Default for Terminator { |
| fn default() -> Terminator { |
| Terminator::CRLF |
| } |
| } |
| |
| /// The quoting style to use when writing CSV data. |
| #[derive(Clone, Copy, Debug)] |
| pub enum QuoteStyle { |
| /// This puts quotes around every field. Always. |
| Always, |
| /// This puts quotes around fields only when necessary. |
| /// |
| /// They are necessary when fields contain a quote, delimiter or record |
| /// terminator. Quotes are also necessary when writing an empty record |
| /// (which is indistinguishable from a record with one empty field). |
| /// |
| /// This is the default. |
| Necessary, |
| /// This puts quotes around all fields that are non-numeric. Namely, when |
| /// writing a field that does not parse as a valid float or integer, then |
| /// quotes will be used even if they aren't strictly necessary. |
| NonNumeric, |
| /// This *never* writes quotes, even if it would produce invalid CSV data. |
| Never, |
| /// Hints that destructuring should not be exhaustive. |
| /// |
| /// This enum may grow additional variants, so this makes sure clients |
| /// don't count on exhaustive matching. (Otherwise, adding a new variant |
| /// could break existing code.) |
| #[doc(hidden)] |
| __Nonexhaustive, |
| } |
| |
| impl Default for QuoteStyle { |
| fn default() -> QuoteStyle { |
| QuoteStyle::Necessary |
| } |
| } |