1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
/*! `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 } }