csv_core/
lib.rs

1/*!
2`csv-core` provides a fast CSV reader and writer for use in a `no_std` context.
3
4This crate will never use the standard library. `no_std` support is therefore
5enabled by default.
6
7If you're looking for more ergonomic CSV parsing routines, please use the
8[`csv`](https://docs.rs/csv) crate.
9
10# Overview
11
12This crate has two primary APIs. The `Reader` API provides a CSV parser, and
13the `Writer` API provides a CSV writer.
14
15# Example: reading CSV
16
17This example shows how to count the number of fields and records in CSV data.
18
19```
20use csv_core::{Reader, ReadFieldResult};
21
22let data = "
23foo,bar,baz
24a,b,c
25xxx,yyy,zzz
26";
27
28let mut rdr = Reader::new();
29let mut bytes = data.as_bytes();
30let mut count_fields = 0;
31let mut count_records = 0;
32loop {
33    // We skip handling the output since we don't need it for counting.
34    let (result, nin, _) = rdr.read_field(bytes, &mut [0; 1024]);
35    bytes = &bytes[nin..];
36    match result {
37        ReadFieldResult::InputEmpty => {},
38        ReadFieldResult::OutputFull => panic!("field too large"),
39        ReadFieldResult::Field { record_end } => {
40            count_fields += 1;
41            if record_end {
42                count_records += 1;
43            }
44        }
45        ReadFieldResult::End => break,
46    }
47}
48assert_eq!(3, count_records);
49assert_eq!(9, count_fields);
50```
51
52# Example: writing CSV
53
54This example shows how to use the `Writer` API to write valid CSV data. Proper
55quoting is handled automatically.
56
57```
58use csv_core::Writer;
59
60// This is where we'll write out CSV data.
61let mut out = &mut [0; 1024];
62// The number of bytes we've written to `out`.
63let mut nout = 0;
64// Create a CSV writer with a default configuration.
65let mut wtr = Writer::new();
66
67// Write a single field. Note that we ignore the `WriteResult` and the number
68// of input bytes consumed since we're doing this by hand.
69let (_, _, n) = wtr.field(&b"foo"[..], &mut out[nout..]);
70nout += n;
71
72// Write a delimiter and then another field that requires quotes.
73let (_, n) = wtr.delimiter(&mut out[nout..]);
74nout += n;
75let (_, _, n) = wtr.field(&b"bar,baz"[..], &mut out[nout..]);
76nout += n;
77let (_, n) = wtr.terminator(&mut out[nout..]);
78nout += n;
79
80// Now write another record.
81let (_, _, n) = wtr.field(&b"a \"b\" c"[..], &mut out[nout..]);
82nout += n;
83let (_, n) = wtr.delimiter(&mut out[nout..]);
84nout += n;
85let (_, _, n) = wtr.field(&b"quux"[..], &mut out[nout..]);
86nout += n;
87
88// We must always call finish once done writing.
89// This ensures that any closing quotes are written.
90let (_, n) = wtr.finish(&mut out[nout..]);
91nout += n;
92
93assert_eq!(&out[..nout], &b"\
94foo,\"bar,baz\"
95\"a \"\"b\"\" c\",quux"[..]);
96```
97*/
98
99#![deny(missing_docs)]
100#![no_std]
101
102pub use crate::reader::{
103    ReadFieldNoCopyResult, ReadFieldResult, ReadRecordNoCopyResult,
104    ReadRecordResult, Reader, ReaderBuilder,
105};
106pub use crate::writer::{
107    is_non_numeric, quote, WriteResult, Writer, WriterBuilder,
108};
109
110mod reader;
111mod writer;
112
113/// A record terminator.
114///
115/// Use this to specify the record terminator while parsing CSV. The default is
116/// CRLF, which treats `\r`, `\n` or `\r\n` as a single record terminator.
117#[derive(Clone, Copy, Debug)]
118pub enum Terminator {
119    /// Parses `\r`, `\n` or `\r\n` as a single record terminator.
120    CRLF,
121    /// Parses the byte given as a record terminator.
122    Any(u8),
123    /// Hints that destructuring should not be exhaustive.
124    ///
125    /// This enum may grow additional variants, so this makes sure clients
126    /// don't count on exhaustive matching. (Otherwise, adding a new variant
127    /// could break existing code.)
128    #[doc(hidden)]
129    __Nonexhaustive,
130}
131
132impl Terminator {
133    /// Checks whether the terminator is set to CRLF.
134    fn is_crlf(&self) -> bool {
135        match *self {
136            Terminator::CRLF => true,
137            Terminator::Any(_) => false,
138            _ => unreachable!(),
139        }
140    }
141
142    fn equals(&self, other: u8) -> bool {
143        match *self {
144            Terminator::CRLF => other == b'\r' || other == b'\n',
145            Terminator::Any(b) => other == b,
146            _ => unreachable!(),
147        }
148    }
149}
150
151impl Default for Terminator {
152    fn default() -> Terminator {
153        Terminator::CRLF
154    }
155}
156
157/// The quoting style to use when writing CSV data.
158#[derive(Clone, Copy, Debug)]
159pub enum QuoteStyle {
160    /// This puts quotes around every field. Always.
161    Always,
162    /// This puts quotes around fields only when necessary.
163    ///
164    /// They are necessary when fields contain a quote, delimiter or record
165    /// terminator. Quotes are also necessary when writing an empty record
166    /// (which is indistinguishable from a record with one empty field).
167    ///
168    /// This is the default.
169    Necessary,
170    /// This puts quotes around all fields that are non-numeric. Namely, when
171    /// writing a field that does not parse as a valid float or integer, then
172    /// quotes will be used even if they aren't strictly necessary.
173    NonNumeric,
174    /// This *never* writes quotes, even if it would produce invalid CSV data.
175    Never,
176    /// Hints that destructuring should not be exhaustive.
177    ///
178    /// This enum may grow additional variants, so this makes sure clients
179    /// don't count on exhaustive matching. (Otherwise, adding a new variant
180    /// could break existing code.)
181    #[doc(hidden)]
182    __Nonexhaustive,
183}
184
185impl Default for QuoteStyle {
186    fn default() -> QuoteStyle {
187        QuoteStyle::Necessary
188    }
189}