1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Strict, bounded, and deterministic CSV for reliability-sensitive Rust.
//!
//! `reliakit-csv` reads and writes a strict subset of [RFC 4180]. It is built
//! for systems that process **untrusted** CSV or need **predictable** output:
//! it rejects malformed quoting, enforces a rectangular shape (every record has
//! the same number of fields), applies explicit [resource limits](CsvLimits),
//! reports errors with a location, and serializes deterministically. It has no
//! external dependencies, forbids unsafe code, and supports `no_std` (with
//! `alloc`).
//!
//! It deliberately does **not** include type inference, configurable dialects,
//! a streaming reader/writer over `std::io`, schema validation, or lenient
//! recovery from malformed input.
//!
//! # Records
//!
//! At the lowest level a CSV document is a sequence of records, and each record
//! is a sequence of UTF-8 string fields. [`read_str`] parses text into
//! `Vec<Vec<String>>` and [`CsvWriter`] turns records back into text.
//!
//! ```
//! use reliakit_csv::{read_str, CsvWriter};
//!
//! let records = read_str("name,city\nAda,London\n").unwrap();
//! assert_eq!(records, [["name", "city"], ["Ada", "London"]]);
//!
//! // Serialization is deterministic: a field is quoted only when it must be,
//! // and every record ends with CRLF.
//! let mut writer = CsvWriter::new();
//! writer.write_record(["a", "b,c"]);
//! assert_eq!(writer.into_string(), "a,\"b,c\"\r\n");
//! ```
//!
//! # Strictness
//!
//! The reader rejects input that lenient parsers would accept, so a successful
//! parse is a strong guarantee:
//!
//! ```
//! use reliakit_csv::read_str;
//!
//! // A quote inside an unquoted field is rejected, not absorbed.
//! assert!(read_str("ab\"c\n").is_err());
//! // Records must be rectangular: a short row is an error, not a `None` cell.
//! assert!(read_str("a,b\nc\n").is_err());
//! // A quoted field that never closes is rejected.
//! assert!(read_str("\"oops\n").is_err());
//! ```
//!
//! # Typed encoding
//!
//! [`CsvEncode`] maps your record type to a row of fields (with a header), and
//! [`CsvDecode`] reads a row back, strictly. [`to_csv_string`] writes a header
//! row followed by one row per value; [`from_csv_str`] validates the header and
//! decodes the rest.
//!
//! ```
//! use reliakit_csv::{
//! from_csv_str, to_csv_string, CsvDecode, CsvDecodeError, CsvEncode, CsvField,
//! };
//! use std::vec::Vec;
//!
//! #[derive(Debug, PartialEq)]
//! struct Row {
//! id: u32,
//! name: String,
//! }
//!
//! impl CsvEncode for Row {
//! fn header() -> Vec<&'static str> {
//! vec!["id", "name"]
//! }
//! fn encode_fields(&self, out: &mut Vec<String>) {
//! out.push(self.id.encode_field());
//! out.push(self.name.encode_field());
//! }
//! }
//!
//! impl CsvDecode for Row {
//! fn decode_fields(fields: &[&str]) -> Result<Self, CsvDecodeError> {
//! if fields.len() != 2 {
//! return Err(CsvDecodeError::field_count());
//! }
//! Ok(Row {
//! id: u32::decode_field(fields[0]).map_err(|e| e.at_field(0))?,
//! name: String::decode_field(fields[1]).map_err(|e| e.at_field(1))?,
//! })
//! }
//! }
//!
//! let rows = vec![Row { id: 1, name: "Ada".into() }];
//! let text = to_csv_string(&rows);
//! assert_eq!(text, "id,name\r\n1,Ada\r\n");
//! assert_eq!(from_csv_str::<Row>(&text).unwrap(), rows);
//! ```
//!
//! The per-field `encode_field`/`decode_field` calls above come from
//! [`CsvField`], which is implemented for the integer types, `bool`, `String`,
//! and `Option<T>`.
//!
//! # Limits
//!
//! [`read_str`] applies conservative [`CsvLimits`] by default. Use
//! [`read_str_with_limits`] to choose a profile or tune individual limits:
//!
//! ```
//! use reliakit_csv::{read_str_with_limits, CsvLimits};
//!
//! let limits = CsvLimits::conservative().with_max_fields_per_record(2);
//! assert!(read_str_with_limits("a,b,c\n", &limits).is_err());
//! ```
//!
//! # Feature flags
//!
//! - `std` (default) enables `std::error::Error` for the error types. The crate
//! is otherwise `no_std` and always uses `alloc`.
//!
//! [RFC 4180]: https://www.rfc-editor.org/rfc/rfc4180
extern crate alloc;
pub use ;
pub use CsvField;
pub use CsvLimits;
pub use ;
pub use ;
pub use CsvWriter;
/// Implementation details used by the `CsvEncode`/`CsvDecode` derives in
/// `reliakit-derive`. Not part of the public API — do not use it directly; it
/// may change at any time. It only re-exports `alloc` types so generated code
/// can name them without assuming they are in scope on `no_std`.