Skip to main content

deser_csv/
lib.rs

1//! CSV, TSV and other delimited text for deser.
2//!
3//! ```rust
4//! use deser::{Deserialize, Serialize};
5//!
6//! #[derive(Debug, Deserialize, Serialize)]
7//! struct City {
8//!     name: String,
9//!     country: String,
10//!     population: Option<u64>,
11//! }
12//!
13//! let input =
14//!     "name,country,population\nVienna,Austria,1897000\nAtlantis,,\n";
15//! let cities: Vec<City> = deser_csv::from_str(input).unwrap();
16//! assert_eq!(cities[0].population, Some(1897000));
17//! assert_eq!(cities[1].population, None);
18//!
19//! assert_eq!(deser_csv::to_string(&cities).unwrap(), input);
20//! ```
21//!
22//! # Data Model
23//!
24//! A document is a sequence of records.  By default the first record holds
25//! the names of the columns (see [`Headers`]) and the other records are
26//! maps of the names to their fields, without names records are sequences
27//! (for instance tuples):
28//!
29//! | input                  | deser                                     |
30//! |------------------------|-------------------------------------------|
31//! | `a,b\n1,2\n3,4\n`      | `[{"a": "1", "b": "2"}, {"a": "3", "b": "4"}]` |
32//! | `1,2\n3,4\n` (no names)| `[["1", "2"], ["3", "4"]]`                |
33//!
34//! Everything in a CSV file is text, only the type a field is deserialized
35//! into knows what it means.  Fields are therefore passed on as [lexical
36//! atoms](deser_core::Atom::Lexical) which are parsed by the types they are
37//! delivered to: numbers parse them, strings take them as they are.  They
38//! are interpreted with the [lenient
39//! rules](deser_core::de::LexicalRules::LENIENT): `yes`, `on` and `1` are
40//! booleans too and empty fields are `None` for optional numbers (other
41//! [`LexicalRules`](deser_core::de::LexicalRules) can be given in the
42//! [`Context`](deser_core::Context)).  This also works when values are buffered, so flattened structs and
43//! internally tagged and untagged enums work:
44//!
45//! ```rust
46//! use deser::Deserialize;
47//!
48//! #[derive(Debug, Deserialize, PartialEq)]
49//! #[deser(tag = "kind", rename_all = "lowercase")]
50//! enum Shape {
51//!     Circle { radius: f64 },
52//!     Rect { width: f64, height: f64 },
53//! }
54//!
55//! #[derive(Debug, Deserialize, PartialEq)]
56//! struct Row {
57//!     id: u32,
58//!     #[deser(flatten)]
59//!     shape: Shape,
60//! }
61//!
62//! let input = "id,kind,radius,width,height\n1,circle,2,,\n2,rect,,3,4\n";
63//! let config =
64//!     deser_csv::DeserializerConfig::builder().nulls(deser_csv::Nulls::Empty).build();
65//! let rows: Vec<Row> = config.from_str(input).unwrap();
66//! assert_eq!(rows[1].shape, Shape::Rect { width: 3.0, height: 4.0 });
67//! ```
68//!
69//! Empty fields are `None` for optionals if the type does not accept them
70//! (an empty field is `None` for an `Option<u32>` and `Some("")` for an
71//! `Option<String>`).  Which fields are null can be configured (see
72//! [`Nulls`]).  Fields cannot hold maps or sequences, but a list in a
73//! field can be read with the [`Separated`](deser_core::adapters::Separated)
74//! adapter (`#[deser(as = Separated<';'>)]` reads `a;b;c`).
75//!
76//! Serializing works the other way around (see [`SerializerConfig`]): the
77//! value is a sequence of records which are maps (the keys of the first
78//! record are the names of the columns) or sequences.
79//!
80//! # Dialects
81//!
82//! There is no single CSV format, the configurations can be adjusted to
83//! what the other side writes and reads:
84//!
85//! * The delimiter (`,`, `;`, `\t`, `|`, the ASCII unit separator, ...),
86//!   the quote character (or no quotes at all) and if quotes are doubled
87//!   or escaped (see [`Escape`]).
88//! * The line ending (see [`Terminator`]), by default `\n`, `\r\n` and
89//!   `\r` all end records and `\n` is written.
90//! * Comment lines, blank lines, whitespace around fields (see [`Trim`]),
91//!   records with a different number of fields (`flexible`) and fields
92//!   with quotes that do not follow the rules (`lenient_quotes`).
93//! * Tab separated values as written by databases (with backslash escapes
94//!   and `\N` for null), see [`DeserializerConfig::tsv`].
95//!
96//! Input is UTF-8, a byte order mark at the start is skipped (and UTF-16
97//! input is reported as such).  Fields which are not UTF-8 are passed on as
98//! bytes.  The `sep=;` line Excel writes can be enabled with
99//! [`DeserializerConfig::set_sep_line`].
100//!
101//! # Errors
102//!
103//! Errors point at the position in the input and (with `deser-path`) at
104//! the path of the value, for instance `[3].price`.  By default quotes
105//! that do not follow the rules and records with the wrong number of fields
106//! are errors.
107//!
108//! # Streams
109//!
110//! A stream of records is read with a reader of `deser::io` which the
111//! configuration creates ([`DeserializerConfig::reader`]): every read
112//! returns the next record.  Errors of a record (like a field that does
113//! not fit the type) only discard the record.  The names of the columns
114//! are known to the stream deserializer (see
115//! [`StreamDeserializer::headers`]).
116//!
117//! ```rust
118//! # #[cfg(feature = "io")] {
119//! use deser_csv::DeserializerConfig;
120//!
121//! #[derive(deser::Deserialize)]
122//! struct Row {
123//!     name: String,
124//!     age: u32,
125//! }
126//!
127//! let input = &b"name,age\njane,42\njohn,x\nmax,7\n"[..];
128//! let mut reader = DeserializerConfig::new().reader(input);
129//! let mut ages = Vec::new();
130//! let mut errors = Vec::new();
131//! loop {
132//!     match reader.read::<Row>() {
133//!         Ok(Some(row)) => ages.push(row.age),
134//!         Ok(None) => break,
135//!         Err(err) => errors.push(err.line()),
136//!     }
137//! }
138//! assert_eq!(ages, [42, 7]);
139//! assert_eq!(errors, [Some(3)]);
140//! assert_eq!(reader.deserializer().headers().unwrap(), ["name", "age"]);
141//! # }
142//! ```
143//!
144//! Likewise every value written with a writer created by
145//! [`SerializerConfig::writer`] is a record, the names are written before
146//! the first one.  The functions that read and write a single value
147//! ([`from_reader`] and [`to_writer`]) read and write all records as a
148//! sequence, like [`from_str`] and [`to_string`].
149//!
150//! The stream serializer ([`Serializer`]) and the stream deserializer
151//! ([`StreamDeserializer`]) do not do IO themselves (see
152//! [`deser::stream`](deser_core::stream)), they also work with other kinds
153//! of IO and without the standard library.
154//!
155//! # Features
156//!
157//! * `io` (enabled by default): reading and writing streams of the
158//!   standard library, see [streams](#streams).  Requires `std`.
159//! * `speedups` (enabled by default): has no effect yet, it exists so
160//!   that all formats have it.
161//! * `std` (enabled by default): uses the standard library.  Without it
162//!   this crate only needs `alloc` (see [`no_std`](https://docs.rs/deser/latest/deser/#no_std)).
163#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
164#![cfg_attr(not(any(feature = "std", test)), no_std)]
165
166extern crate alloc;
167
168mod de;
169mod parser;
170mod ser;
171mod stream;
172
173pub use self::de::{Deserializer, DeserializerConfig, DeserializerConfigBuilder, Records};
174#[cfg(feature = "io")]
175pub use self::ser::to_writer;
176pub use self::ser::{Serializer, SerializerConfig, SerializerConfigBuilder, to_string};
177pub use self::stream::StreamDeserializer;
178#[cfg(feature = "io")]
179pub use self::stream::from_reader;
180
181use deser_core::Error;
182use deser_core::de::Deserialize;
183
184/// Where the names of the columns come from.
185///
186/// ```
187/// use std::collections::BTreeMap;
188/// use deser_csv::{DeserializerConfig, Headers};
189///
190/// let rows: Vec<BTreeMap<String, u32>> =
191///     deser_csv::from_str("a,b\n1,2\n").unwrap();
192/// assert_eq!(rows[0]["b"], 2);
193///
194/// let config = DeserializerConfig::builder().headers(Headers::None).build();
195/// let rows: Vec<Vec<u32>> = config.from_str("1,2\n3,4\n").unwrap();
196/// assert_eq!(rows, [[1, 2], [3, 4]]);
197///
198/// let config = DeserializerConfig::builder().headers(Headers::Skip).build();
199/// let rows: Vec<(String, u32)> =
200///     config.from_str("name,age\njane,42\n").unwrap();
201/// assert_eq!(rows, [("jane".to_string(), 42)]);
202///
203/// let config =
204///     DeserializerConfig::builder().headers(Headers::Given(&["a", "b"])).build();
205/// let rows: Vec<BTreeMap<String, u32>> = config.from_str("1,2\n").unwrap();
206/// assert_eq!(rows[0]["a"], 1);
207/// ```
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
209#[non_exhaustive]
210pub enum Headers {
211    /// The first record holds the names, records are maps.
212    #[default]
213    First,
214    /// The first record holds the names, but records are sequences (for
215    /// instance to read them into tuples).
216    ///
217    /// The names are still read (see [`StreamDeserializer::headers`]).
218    Skip,
219    /// There are no names, records are sequences.
220    None,
221    /// The names are given (the first record is a regular record), records
222    /// are maps.
223    Given(&'static [&'static str]),
224}
225
226/// What ends records.
227///
228/// Line breaks in quoted fields do not end records.
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
230#[non_exhaustive]
231pub enum Terminator {
232    /// Records end with `\n`, `\r\n` or `\r` (also mixed).  `\n` is
233    /// written.
234    #[default]
235    Newline,
236    /// Like [`Newline`](Self::Newline) but `\r\n` is written (as described
237    /// by RFC 4180).
238    CrLf,
239    /// Records end with the given character (for instance the ASCII record
240    /// separator `0x1e`).
241    Byte(u8),
242}
243
244/// How characters are escaped.
245///
246/// ```
247/// use deser_csv::{DeserializerConfig, Escape, Headers};
248///
249/// let config = DeserializerConfig::builder()
250///     .headers(Headers::None)
251///     .escape(Escape::Backslash)
252///     .double_quote(false).build();
253/// let rows: Vec<Vec<String>> = config.from_str(r#""a\"b",c\,d"#).unwrap();
254/// assert_eq!(rows, [["a\"b", "c,d"]]);
255/// ```
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
257#[non_exhaustive]
258pub enum Escape {
259    /// Nothing is escaped (quotes in quoted fields are doubled).
260    #[default]
261    None,
262    /// The character after this one is taken as it is (in quoted and
263    /// unquoted fields), like `escapechar` of Python's `csv` module.
264    Char(u8),
265    /// Backslash escapes: `\t`, `\n`, `\r` and `\0` are a tab, line feed,
266    /// carriage return and NUL, a backslash followed by another character
267    /// is that character (`\\`, `\"`, `\,`).  This is how databases (like
268    /// PostgreSQL and MySQL) write delimited text.
269    Backslash,
270}
271
272impl Escape {
273    /// Returns the escape character.
274    pub(crate) const fn byte(self) -> Option<u8> {
275        match self {
276            Escape::None => None,
277            Escape::Char(byte) => Some(byte),
278            Escape::Backslash => Some(b'\\'),
279        }
280    }
281}
282
283/// Which whitespace is removed.
284///
285/// Spaces and tabs are removed from the start and end of unquoted fields
286/// and around the quotes of quoted fields (`a, "b" ,c`), whitespace in
287/// quotes is kept.
288///
289/// ```
290/// use deser_csv::{DeserializerConfig, Trim};
291///
292/// #[derive(deser::Deserialize)]
293/// struct Row {
294///     name: String,
295///     age: u32,
296/// }
297///
298/// let config = DeserializerConfig::builder().trim(Trim::All).build();
299/// let rows: Vec<Row> =
300///     config.from_str("name , age\n \"jane \" , 42\n").unwrap();
301/// assert_eq!((rows[0].name.as_str(), rows[0].age), ("jane ", 42));
302/// ```
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
304#[non_exhaustive]
305pub enum Trim {
306    /// Whitespace is kept.
307    #[default]
308    None,
309    /// Whitespace is removed from the names of the columns.
310    Headers,
311    /// Whitespace is removed from the fields of records.
312    Fields,
313    /// Whitespace is removed from names and fields.
314    All,
315}
316
317/// Which fields are null.
318///
319/// Only unquoted fields are null, which allows writing the empty string
320/// (or the text of null) in quotes.
321///
322/// ```
323/// use deser_csv::{DeserializerConfig, Headers, Nulls};
324///
325/// let mut config = DeserializerConfig::builder().headers(Headers::None).build();
326/// let rows: Vec<Vec<Option<String>>> = config.from_str(",\"\"\n").unwrap();
327/// assert_eq!(rows, [[Some("".to_string()), Some("".to_string())]]);
328///
329/// config.set_nulls(Nulls::Empty);
330/// let rows: Vec<Vec<Option<String>>> = config.from_str(",\"\"\n").unwrap();
331/// assert_eq!(rows, [[None, Some("".to_string())]]);
332/// ```
333#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
334#[non_exhaustive]
335pub enum Nulls {
336    /// No field is null.  Empty fields are still `None` for optionals of
337    /// types that do not accept the empty string.
338    #[default]
339    None,
340    /// Empty fields are null (like PostgreSQL's CSV format).
341    Empty,
342    /// Fields with this text are null (like `\N` or `NULL`).
343    Text(&'static str),
344}
345
346/// When fields are quoted.
347///
348/// ```
349/// use deser_csv::{QuoteStyle, SerializerConfig};
350///
351/// let rows = vec![("a b", 1), ("c,d", 2)];
352/// let with = |style| {
353///     SerializerConfig::builder().quote_style(style).build().to_string(&rows).unwrap()
354/// };
355/// assert_eq!(with(QuoteStyle::Necessary), "a b,1\n\"c,d\",2\n");
356/// assert_eq!(with(QuoteStyle::Always), "\"a b\",\"1\"\n\"c,d\",\"2\"\n");
357/// assert_eq!(with(QuoteStyle::NonNumeric), "\"a b\",1\n\"c,d\",2\n");
358/// ```
359#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
360#[non_exhaustive]
361pub enum QuoteStyle {
362    /// Fields are quoted if they have to be (if they contain special
363    /// characters or would otherwise be read differently).
364    #[default]
365    Necessary,
366    /// All fields are quoted.
367    Always,
368    /// All fields except for numbers are quoted.
369    NonNumeric,
370    /// Fields are never quoted, fields that need quotes are an error
371    /// (unless they can be escaped, see [`Escape`]).
372    Never,
373}
374
375/// Deserializes the records of a string.
376///
377/// This uses the default [`DeserializerConfig`] (CSV with a header), see
378/// the [crate documentation](crate) for how records map onto deser.
379///
380/// ```
381/// #[derive(deser::Deserialize)]
382/// struct Row {
383///     name: String,
384///     score: f64,
385/// }
386///
387/// let rows: Vec<Row> =
388///     deser_csv::from_str("name,score\na,1.5\nb,2\n").unwrap();
389/// assert_eq!(rows[1].score, 2.0);
390/// ```
391pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, Error> {
392    DeserializerConfig::new().from_str(s)
393}
394
395/// Deserializes the records of a byte slice.
396///
397/// Fields which are not UTF-8 are passed on as bytes.
398///
399/// ```
400/// use std::collections::BTreeMap;
401///
402/// let rows: Vec<BTreeMap<String, u32>> =
403///     deser_csv::from_slice(b"a,b\n1,2\n").unwrap();
404/// assert_eq!(rows[0]["b"], 2);
405/// ```
406pub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
407    DeserializerConfig::new().from_slice(bytes)
408}