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 copy;
169mod de;
170mod parser;
171mod ser;
172mod stream;
173
174pub use self::de::{Deserializer, DeserializerConfig, DeserializerConfigBuilder, Records};
175#[cfg(feature = "io")]
176pub use self::ser::to_writer;
177pub use self::ser::{Serializer, SerializerConfig, SerializerConfigBuilder, to_string};
178pub use self::stream::StreamDeserializer;
179#[cfg(feature = "io")]
180pub use self::stream::from_reader;
181
182use deser_core::Error;
183use deser_core::de::Deserialize;
184
185/// Where the names of the columns come from.
186///
187/// ```
188/// use std::collections::BTreeMap;
189/// use deser_csv::{DeserializerConfig, Headers};
190///
191/// let rows: Vec<BTreeMap<String, u32>> =
192/// deser_csv::from_str("a,b\n1,2\n").unwrap();
193/// assert_eq!(rows[0]["b"], 2);
194///
195/// let config = DeserializerConfig::builder().headers(Headers::None).build();
196/// let rows: Vec<Vec<u32>> = config.from_str("1,2\n3,4\n").unwrap();
197/// assert_eq!(rows, [[1, 2], [3, 4]]);
198///
199/// let config = DeserializerConfig::builder().headers(Headers::Skip).build();
200/// let rows: Vec<(String, u32)> =
201/// config.from_str("name,age\njane,42\n").unwrap();
202/// assert_eq!(rows, [("jane".to_string(), 42)]);
203///
204/// let config =
205/// DeserializerConfig::builder().headers(Headers::Given(&["a", "b"])).build();
206/// let rows: Vec<BTreeMap<String, u32>> = config.from_str("1,2\n").unwrap();
207/// assert_eq!(rows[0]["a"], 1);
208/// ```
209#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
210#[non_exhaustive]
211pub enum Headers {
212 /// The first record holds the names, records are maps.
213 #[default]
214 First,
215 /// The first record holds the names, but records are sequences (for
216 /// instance to read them into tuples).
217 ///
218 /// The names are still read (see [`StreamDeserializer::headers`]).
219 Skip,
220 /// There are no names, records are sequences.
221 None,
222 /// The names are given (the first record is a regular record), records
223 /// are maps.
224 Given(&'static [&'static str]),
225}
226
227/// What ends records.
228///
229/// Line breaks in quoted fields do not end records.
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
231#[non_exhaustive]
232pub enum Terminator {
233 /// Records end with `\n`, `\r\n` or `\r` (also mixed). `\n` is
234 /// written.
235 #[default]
236 Newline,
237 /// Like [`Newline`](Self::Newline) but `\r\n` is written (as described
238 /// by RFC 4180).
239 CrLf,
240 /// Records end with the given character (for instance the ASCII record
241 /// separator `0x1e`).
242 Byte(u8),
243}
244
245/// How characters are escaped.
246///
247/// ```
248/// use deser_csv::{DeserializerConfig, Escape, Headers};
249///
250/// let config = DeserializerConfig::builder()
251/// .headers(Headers::None)
252/// .escape(Escape::Backslash)
253/// .double_quote(false).build();
254/// let rows: Vec<Vec<String>> = config.from_str(r#""a\"b",c\,d"#).unwrap();
255/// assert_eq!(rows, [["a\"b", "c,d"]]);
256/// ```
257#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
258#[non_exhaustive]
259pub enum Escape {
260 /// Nothing is escaped (quotes in quoted fields are doubled).
261 #[default]
262 None,
263 /// The character after this one is taken as it is (in quoted and
264 /// unquoted fields), like `escapechar` of Python's `csv` module.
265 Char(u8),
266 /// Backslash escapes: `\t`, `\n`, `\r` and `\0` are a tab, line feed,
267 /// carriage return and NUL, a backslash followed by another character
268 /// is that character (`\\`, `\"`, `\,`). This is how databases (like
269 /// PostgreSQL and MySQL) write delimited text.
270 Backslash,
271}
272
273impl Escape {
274 /// Returns the escape character.
275 pub(crate) const fn byte(self) -> Option<u8> {
276 match self {
277 Escape::None => None,
278 Escape::Char(byte) => Some(byte),
279 Escape::Backslash => Some(b'\\'),
280 }
281 }
282}
283
284/// Which whitespace is removed.
285///
286/// Spaces and tabs are removed from the start and end of unquoted fields
287/// and around the quotes of quoted fields (`a, "b" ,c`), whitespace in
288/// quotes is kept.
289///
290/// ```
291/// use deser_csv::{DeserializerConfig, Trim};
292///
293/// #[derive(deser::Deserialize)]
294/// struct Row {
295/// name: String,
296/// age: u32,
297/// }
298///
299/// let config = DeserializerConfig::builder().trim(Trim::All).build();
300/// let rows: Vec<Row> =
301/// config.from_str("name , age\n \"jane \" , 42\n").unwrap();
302/// assert_eq!((rows[0].name.as_str(), rows[0].age), ("jane ", 42));
303/// ```
304#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
305#[non_exhaustive]
306pub enum Trim {
307 /// Whitespace is kept.
308 #[default]
309 None,
310 /// Whitespace is removed from the names of the columns.
311 Headers,
312 /// Whitespace is removed from the fields of records.
313 Fields,
314 /// Whitespace is removed from names and fields.
315 All,
316}
317
318/// Which fields are null.
319///
320/// Only unquoted fields are null, which allows writing the empty string
321/// (or the text of null) in quotes.
322///
323/// ```
324/// use deser_csv::{DeserializerConfig, Headers, Nulls};
325///
326/// let mut config = DeserializerConfig::builder().headers(Headers::None).build();
327/// let rows: Vec<Vec<Option<String>>> = config.from_str(",\"\"\n").unwrap();
328/// assert_eq!(rows, [[Some("".to_string()), Some("".to_string())]]);
329///
330/// config.set_nulls(Nulls::Empty);
331/// let rows: Vec<Vec<Option<String>>> = config.from_str(",\"\"\n").unwrap();
332/// assert_eq!(rows, [[None, Some("".to_string())]]);
333/// ```
334#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
335#[non_exhaustive]
336pub enum Nulls {
337 /// No field is null. Empty fields are still `None` for optionals of
338 /// types that do not accept the empty string.
339 #[default]
340 None,
341 /// Empty fields are null (like PostgreSQL's CSV format).
342 Empty,
343 /// Fields with this text are null (like `\N` or `NULL`).
344 Text(&'static str),
345}
346
347/// When fields are quoted.
348///
349/// ```
350/// use deser_csv::{QuoteStyle, SerializerConfig};
351///
352/// let rows = vec![("a b", 1), ("c,d", 2)];
353/// let with = |style| {
354/// SerializerConfig::builder().quote_style(style).build().to_string(&rows).unwrap()
355/// };
356/// assert_eq!(with(QuoteStyle::Necessary), "a b,1\n\"c,d\",2\n");
357/// assert_eq!(with(QuoteStyle::Always), "\"a b\",\"1\"\n\"c,d\",\"2\"\n");
358/// assert_eq!(with(QuoteStyle::NonNumeric), "\"a b\",1\n\"c,d\",2\n");
359/// ```
360#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
361#[non_exhaustive]
362pub enum QuoteStyle {
363 /// Fields are quoted if they have to be (if they contain special
364 /// characters or would otherwise be read differently).
365 #[default]
366 Necessary,
367 /// All fields are quoted.
368 Always,
369 /// All fields except for numbers are quoted.
370 NonNumeric,
371 /// Fields are never quoted, fields that need quotes are an error
372 /// (unless they can be escaped, see [`Escape`]).
373 Never,
374}
375
376/// Deserializes the records of a string.
377///
378/// This uses the default [`DeserializerConfig`] (CSV with a header), see
379/// the [crate documentation](crate) for how records map onto deser.
380///
381/// ```
382/// #[derive(deser::Deserialize)]
383/// struct Row {
384/// name: String,
385/// score: f64,
386/// }
387///
388/// let rows: Vec<Row> =
389/// deser_csv::from_str("name,score\na,1.5\nb,2\n").unwrap();
390/// assert_eq!(rows[1].score, 2.0);
391/// ```
392pub fn from_str<'de, T: Deserialize<'de>>(s: &'de str) -> Result<T, Error> {
393 DeserializerConfig::new().from_str(s)
394}
395
396/// Deserializes the records of a byte slice.
397///
398/// Fields which are not UTF-8 are passed on as bytes.
399///
400/// ```
401/// use std::collections::BTreeMap;
402///
403/// let rows: Vec<BTreeMap<String, u32>> =
404/// deser_csv::from_slice(b"a,b\n1,2\n").unwrap();
405/// assert_eq!(rows[0]["b"], 2);
406/// ```
407pub fn from_slice<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
408 DeserializerConfig::new().from_slice(bytes)
409}