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