Coverage Report

Created: 2026-05-16 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/csv-1.4.0/src/lib.rs
Line
Count
Source
1
/*!
2
The `csv` crate provides a fast and flexible CSV reader and writer, with
3
support for Serde.
4
5
The [tutorial](tutorial/index.html) is a good place to start if you're new to
6
Rust.
7
8
The [cookbook](cookbook/index.html) will give you a variety of complete Rust
9
programs that do CSV reading and writing.
10
11
# Brief overview
12
13
**If you're new to Rust**, you might find the
14
[tutorial](tutorial/index.html)
15
to be a good place to start.
16
17
The primary types in this crate are
18
[`Reader`](struct.Reader.html)
19
and
20
[`Writer`](struct.Writer.html),
21
for reading and writing CSV data respectively.
22
Correspondingly, to support CSV data with custom field or record delimiters
23
(among many other things), you should use either a
24
[`ReaderBuilder`](struct.ReaderBuilder.html)
25
or a
26
[`WriterBuilder`](struct.WriterBuilder.html),
27
depending on whether you're reading or writing CSV data.
28
29
Unless you're using Serde, the standard CSV record types are
30
[`StringRecord`](struct.StringRecord.html)
31
and
32
[`ByteRecord`](struct.ByteRecord.html).
33
`StringRecord` should be used when you know your data to be valid UTF-8.
34
For data that may be invalid UTF-8, `ByteRecord` is suitable.
35
36
Finally, the set of errors is described by the
37
[`Error`](struct.Error.html)
38
type.
39
40
The rest of the types in this crate mostly correspond to more detailed errors,
41
position information, configuration knobs or iterator types.
42
43
# Setup
44
45
Run `cargo add csv` to add the latest version of the `csv` crate to your
46
Cargo.toml.
47
48
If you want to use Serde's custom derive functionality on your custom structs,
49
then run `cargo add serde --features derive` to add the `serde` crate with its
50
`derive` feature enabled to your `Cargo.toml`.
51
52
# Example
53
54
This example shows how to read CSV data from stdin and print each record to
55
stdout.
56
57
There are more examples in the [cookbook](cookbook/index.html).
58
59
```no_run
60
use std::{error::Error, io, process};
61
62
fn example() -> Result<(), Box<dyn Error>> {
63
    // Build the CSV reader and iterate over each record.
64
    let mut rdr = csv::Reader::from_reader(io::stdin());
65
    for result in rdr.records() {
66
        // The iterator yields Result<StringRecord, Error>, so we check the
67
        // error here.
68
        let record = result?;
69
        println!("{:?}", record);
70
    }
71
    Ok(())
72
}
73
74
fn main() {
75
    if let Err(err) = example() {
76
        println!("error running example: {}", err);
77
        process::exit(1);
78
    }
79
}
80
```
81
82
The above example can be run like so:
83
84
```ignore
85
$ git clone git://github.com/BurntSushi/rust-csv
86
$ cd rust-csv
87
$ cargo run --example cookbook-read-basic < examples/data/smallpop.csv
88
```
89
90
# Example with Serde
91
92
This example shows how to read CSV data from stdin into your own custom struct.
93
By default, the member names of the struct are matched with the values in the
94
header record of your CSV data.
95
96
```no_run
97
use std::{error::Error, io, process};
98
99
#[derive(Debug, serde::Deserialize)]
100
struct Record {
101
    city: String,
102
    region: String,
103
    country: String,
104
    population: Option<u64>,
105
}
106
107
fn example() -> Result<(), Box<dyn Error>> {
108
    let mut rdr = csv::Reader::from_reader(io::stdin());
109
    for result in rdr.deserialize() {
110
        // Notice that we need to provide a type hint for automatic
111
        // deserialization.
112
        let record: Record = result?;
113
        println!("{:?}", record);
114
    }
115
    Ok(())
116
}
117
118
fn main() {
119
    if let Err(err) = example() {
120
        println!("error running example: {}", err);
121
        process::exit(1);
122
    }
123
}
124
```
125
126
The above example can be run like so:
127
128
```ignore
129
$ git clone git://github.com/BurntSushi/rust-csv
130
$ cd rust-csv
131
$ cargo run --example cookbook-read-serde < examples/data/smallpop.csv
132
```
133
134
*/
135
136
#![deny(missing_docs)]
137
138
use std::result;
139
140
use serde_core::{Deserialize, Deserializer};
141
142
pub use crate::{
143
    byte_record::{ByteRecord, ByteRecordIter, Position},
144
    deserializer::{DeserializeError, DeserializeErrorKind},
145
    error::{
146
        Error, ErrorKind, FromUtf8Error, IntoInnerError, Result, Utf8Error,
147
    },
148
    reader::{
149
        ByteRecordsIntoIter, ByteRecordsIter, DeserializeRecordsIntoIter,
150
        DeserializeRecordsIter, Reader, ReaderBuilder, StringRecordsIntoIter,
151
        StringRecordsIter,
152
    },
153
    string_record::{StringRecord, StringRecordIter},
154
    writer::{Writer, WriterBuilder},
155
};
156
157
mod byte_record;
158
pub mod cookbook;
159
mod debug;
160
mod deserializer;
161
mod error;
162
mod reader;
163
mod serializer;
164
mod string_record;
165
pub mod tutorial;
166
mod writer;
167
168
/// The quoting style to use when writing CSV data.
169
#[derive(Clone, Copy, Debug, Default)]
170
#[non_exhaustive]
171
pub enum QuoteStyle {
172
    /// This puts quotes around every field. Always.
173
    Always,
174
    /// This puts quotes around fields only when necessary.
175
    ///
176
    /// They are necessary when fields contain a quote, delimiter or record
177
    /// terminator. Quotes are also necessary when writing an empty record
178
    /// (which is indistinguishable from a record with one empty field).
179
    ///
180
    /// This is the default.
181
    #[default]
182
    Necessary,
183
    /// This puts quotes around all fields that are non-numeric. Namely, when
184
    /// writing a field that does not parse as a valid float or integer, then
185
    /// quotes will be used even if they aren't strictly necessary.
186
    NonNumeric,
187
    /// This *never* writes quotes, even if it would produce invalid CSV data.
188
    Never,
189
}
190
191
impl QuoteStyle {
192
0
    fn to_core(self) -> csv_core::QuoteStyle {
193
0
        match self {
194
0
            QuoteStyle::Always => csv_core::QuoteStyle::Always,
195
0
            QuoteStyle::Necessary => csv_core::QuoteStyle::Necessary,
196
0
            QuoteStyle::NonNumeric => csv_core::QuoteStyle::NonNumeric,
197
0
            QuoteStyle::Never => csv_core::QuoteStyle::Never,
198
        }
199
0
    }
200
}
201
202
/// A record terminator.
203
///
204
/// Use this to specify the record terminator while parsing CSV. The default is
205
/// CRLF, which treats `\r`, `\n` or `\r\n` as a single record terminator.
206
#[derive(Clone, Copy, Debug, Default)]
207
#[non_exhaustive]
208
pub enum Terminator {
209
    /// Parses `\r`, `\n` or `\r\n` as a single record terminator.
210
    #[default]
211
    CRLF,
212
    /// Parses the byte given as a record terminator.
213
    Any(u8),
214
}
215
216
impl Terminator {
217
    /// Convert this to the csv_core type of the same name.
218
0
    fn to_core(self) -> csv_core::Terminator {
219
0
        match self {
220
0
            Terminator::CRLF => csv_core::Terminator::CRLF,
221
0
            Terminator::Any(b) => csv_core::Terminator::Any(b),
222
        }
223
0
    }
224
}
225
226
/// The whitespace preservation behaviour when reading CSV data.
227
#[derive(Clone, Copy, Debug, Default, PartialEq)]
228
#[non_exhaustive]
229
pub enum Trim {
230
    /// Preserves fields and headers. This is the default.
231
    #[default]
232
    None,
233
    /// Trim whitespace from headers.
234
    Headers,
235
    /// Trim whitespace from fields, but not headers.
236
    Fields,
237
    /// Trim whitespace from fields and headers.
238
    All,
239
}
240
241
impl Trim {
242
0
    fn should_trim_fields(&self) -> bool {
243
0
        self == &Trim::Fields || self == &Trim::All
244
0
    }
245
246
0
    fn should_trim_headers(&self) -> bool {
247
0
        self == &Trim::Headers || self == &Trim::All
248
0
    }
249
}
250
251
/// A custom Serde deserializer for possibly invalid `Option<T>` fields.
252
///
253
/// When deserializing CSV data, it is sometimes desirable to simply ignore
254
/// fields with invalid data. For example, there might be a field that is
255
/// usually a number, but will occasionally contain garbage data that causes
256
/// number parsing to fail.
257
///
258
/// You might be inclined to use, say, `Option<i32>` for fields such at this.
259
/// By default, however, `Option<i32>` will either capture *empty* fields with
260
/// `None` or valid numeric fields with `Some(the_number)`. If the field is
261
/// non-empty and not a valid number, then deserialization will return an error
262
/// instead of using `None`.
263
///
264
/// This function allows you to override this default behavior. Namely, if
265
/// `Option<T>` is deserialized with non-empty but invalid data, then the value
266
/// will be `None` and the error will be ignored.
267
///
268
/// # Example
269
///
270
/// This example shows how to parse CSV records with numerical data, even if
271
/// some numerical data is absent or invalid. Without the
272
/// `serde(deserialize_with = "...")` annotations, this example would return
273
/// an error.
274
///
275
/// ```
276
/// use std::error::Error;
277
///
278
/// #[derive(Debug, serde::Deserialize, Eq, PartialEq)]
279
/// struct Row {
280
///     #[serde(deserialize_with = "csv::invalid_option")]
281
///     a: Option<i32>,
282
///     #[serde(deserialize_with = "csv::invalid_option")]
283
///     b: Option<i32>,
284
///     #[serde(deserialize_with = "csv::invalid_option")]
285
///     c: Option<i32>,
286
/// }
287
///
288
/// # fn main() { example().unwrap(); }
289
/// fn example() -> Result<(), Box<dyn Error>> {
290
///     let data = "\
291
/// a,b,c
292
/// 5,\"\",xyz
293
/// ";
294
///     let mut rdr = csv::Reader::from_reader(data.as_bytes());
295
///     if let Some(result) = rdr.deserialize().next() {
296
///         let record: Row = result?;
297
///         assert_eq!(record, Row { a: Some(5), b: None, c: None });
298
///         Ok(())
299
///     } else {
300
///         Err(From::from("expected at least one record but got none"))
301
///     }
302
/// }
303
/// ```
304
0
pub fn invalid_option<'de, D, T>(de: D) -> result::Result<Option<T>, D::Error>
305
0
where
306
0
    D: Deserializer<'de>,
307
0
    Option<T>: Deserialize<'de>,
308
{
309
0
    Option::<T>::deserialize(de).or_else(|_| Ok(None))
310
0
}