csv/csv.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! A fast, low-allocation CSV parser with `no_std` and serde support.
4//!
5//! # Quick start
6//!
7//! ```rust
8//! let data: &[u8] = b"name,age,city\nAlice,30,NYC\nBob,25,LA\n";
9//! let mut reader = csv::Reader::new(data);
10//!
11//! for row in reader.rows() {
12//! for field in row.iter() {
13//! let field: &str = field.unwrap();
14//! }
15//! let fields: Vec<&str> = row.to_vec().unwrap();
16//! }
17//! ```
18//!
19//! # Features
20//!
21//! | Flag | Default | Description |
22//! |------|---------|-------------|
23//! | `std` | on | Enables `std::io::Read` support for `Reader`, and `Writer` impls. |
24//! | `serde` | off | Enables `Row::deserialize()` + `Writer::serialize()` for serde support. |
25//!
26//! # Parsing rules
27//!
28//! * `\r\n`, `\n` and a bare `\r` all terminate a record.
29//! * Blank lines are skipped.
30//! * A record with a different field count than the first one is an error
31//! unless [`Reader::set_flexible`] is enabled.
32//! * Content after a closing quote (`"a"x`) and an unterminated quote are
33//! errors, not silently accepted. When a record is malformed the reader skips
34//! to the next record boundary, so one bad record does not corrupt the rest
35//! of the input.
36//! * A UTF-8 byte-order mark at the very start of the input is skipped, once.
37//! A BOM anywhere else is data. Pass [`Reader::set_strip_bom`]`(false)` to keep
38//! it; [`Writer::set_write_bom`]`(true)` emits one.
39//!
40//! # Streaming from any [`Read`] source
41//!
42//! ```no_run
43//! # #[cfg(feature = "std")] {
44//! use std::fs::File;
45//! use csv::Reader;
46//!
47//! let file = File::open("data.csv")?;
48//! let mut reader = Reader::new(file);
49//!
50//! for row in reader.rows() {
51//! let name = row.get(0).unwrap().unwrap();
52//! println!("{name}");
53//! }
54//! # }
55//! # Ok::<_, csv::ReadError>(())
56//! ```
57//!
58//! # Headers
59//!
60//! ```no_run
61//! # #[cfg(feature = "std")] {
62//! use csv::Reader;
63//! let data = b"name,age\nAlice,30\n";
64//! let mut reader = Reader::new(std::io::Cursor::new(data));
65//! let headers = reader.parse_headers()?;
66//! for row in reader.rows() {
67//! }
68//! # }
69//! # Ok::<_, csv::ReadError>(())
70//! ```
71//!
72//! # Serde (requires `serde` feature)
73//!
74//! ```no_run
75//! # #[cfg(feature = "serde")] {
76//! use csv::Reader;
77//! use serde::Deserialize;
78//!
79//! #[derive(Deserialize)]
80//! struct Record {
81//! name: String,
82//! age: u32,
83//! }
84//!
85//! let mut reader = Reader::new(std::io::Cursor::new(b"name,age\nAlice,30\n"));
86//! reader.parse_headers()?;
87//!
88//! for row in reader.rows() {
89//! let rec: Record = row.deserialize()?;
90//! println!("{} is {}", rec.name, rec.age);
91//! }
92//! # }
93//! # Ok::<_, csv::ReadError>(())
94//! ```
95//!
96//! # Writer
97//!
98//! ```no_run
99//! use csv::Writer;
100//!
101//! let mut w = Writer::new(Vec::new());
102//! w.write_headers(["name", "age"])?;
103//! w.write_row(["Alice", "30"])?;
104//! let bytes = w.into_inner()?;
105//! # Ok::<_, csv::WriteError>(())
106//! ```
107//!
108//! With serde:
109//!
110//! ```no_run
111//! # #[cfg(feature = "serde")] {
112//! use csv::Writer;
113//! use serde::Serialize;
114//!
115//! #[derive(Serialize)]
116//! struct Person { name: String, age: u32 }
117//!
118//! let mut w = Writer::new(Vec::new())
119//! .set_headers(vec!["name".into(), "age".into()]);
120//! let p = Person { name: "Alice".into(), age: 30 };
121//! w.serialize(&p)?;
122//! # }
123//! # Ok::<_, csv::WriteError>(())
124//! ```
125//!
126//! # Security
127//!
128//! By default the reader applies **no size limits**: a single crafted record
129//! can make it buffer an unbounded amount of memory. Applications that parse
130//! untrusted input should set [`Reader::set_max_record_bytes`], and optionally
131//! [`Reader::set_max_field_bytes`] and [`Reader::set_max_columns`]. Exceeding a
132//! limit yields an error on the row and stops reading, rather than exhausting
133//! memory or reading the rest of an oversized record.
134//!
135//! The writer does not defend against CSV / formula injection (CWE-1236); see
136//! [`Writer`] for details.
137//!
138//! # Design
139//!
140//! * **Owned rows**: `Row` and `BytesRow` own their data. Rows can outlive the `Reader`.
141//! * **Single buffer**: Each row stores all fields contiguously in one `Vec<u8>`.
142//! * **`memchr` scanning**: uses `memchr3` to bulk-scan unquoted fields for
143//! delimiters, quotes and newlines, and `memchr` to scan quoted fields.
144//! * **Deferred errors**: Row parsing errors are stored in the row and surfaced
145//! by every accessor (`get`, `to_vec`, `iter`, `error`). I/O errors are also
146//! stored on the row.
147//! * **BytesRow / Row split**: `BytesRow` works with raw bytes; `Row` adds UTF-8 validation.
148//! * **Streaming**: rows are parsed on-demand from any [`Read`] source, with a
149//! single reusable input buffer that is compacted amortised rather than once
150//! per record.
151//!
152//! # Performance
153//!
154//! Parsing is a single pass over the input with `memchr`-accelerated scanning.
155//! The reader's input buffer is only compacted once a meaningful amount of it
156//! has been consumed, so throughput does not degrade for inputs made of many
157//! small records. Each row allocates two small buffers (its field bytes and its
158//! field offsets); the writer reuses one buffer and allocates nothing per row.
159
160extern crate alloc;
161
162#[cfg(feature = "std")]
163extern crate std;
164
165mod error;
166mod reader;
167
168mod writer;
169
170#[cfg(feature = "serde")]
171mod serde;
172
173/// The UTF-8 byte-order mark (`U+FEFF`).
174pub(crate) const BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
175
176pub use error::{ReadError, ReadErrorKind, WriteError};
177pub use reader::{BytesFields, BytesRow, BytesRows, FieldRange, Fields, Read, Reader, Row, Rows};
178pub use writer::{Write, Writer};