Module encoding/csv
CSV (RFC 4180) reader and writer.
A document is a list of records, a record a list of fields — Strings,
byte-indexed like every other String. The reader accepts LF or CRLF
record terminators, quoted fields ("...") with "" for an embedded
quote and any bytes — delimiters, quotes, newlines — inside the quotes,
and a final record with or without a terminator. The writer quotes a
field only when it must (it contains the delimiter, a quote, CR or LF),
so csv.parse(csv.write(rows)) round-trips.
Errors are a typed enum (CsvError) carrying byte positions, per the D1
convention for pure decoders (plans/archive/STD_API_AUDIT.md).
csv :: import("std/encoding/csv");
rows := csv.parse(String.from("a,b\n1,\"x,y\"\n")); // Result(ArrayList(ArrayList(String)), CsvError)
text := csv.write(rows.unwrap()); // `a,b\n1,"x,y"\n`
Stability
stable — frozen in #421, which reviewed the four expired unstable windows
and decided this one. The whole module is eight names (csv_parse,
csv_parse_with, csv_parse_strict, csv_write, csv_write_with,
CsvOptions, LineEnding, CsvError), each covered by
tests/encoding/csv.test.yo, and the shape questions a CSV reader has —
whether a lenient parse or a strict one is the default, whether errors are
values or effects, what a quote fault reports — are all answered and
answered the way Rust's csv crate answers them.
There is deliberately NO header-record API: Reader::headers(),
StringRecord and serde derive are the parts of Rust's csv this does
not have, and adding them is ADDITIVE — a record here is an
ArrayList(String) whether or not the first one names columns, so a
future header layer sits on top of csv_parse without changing it. The
one place to be careful is CsvOptions: it is a plain value struct with
public fields, so a new option is a new field and every positional
CsvOptions(...) literal names its fields — use default() or
with_delimiter() and let new options default.
LineEnding.Lf as the writer default is the module's one divergence from
RFC 4180 (which says CRLF) and is documented at default().
Types
A CSV decoding failure. Positions are byte offsets into the input.
Variants
| Variant | Fields | Description |
|---|---|---|
UnexpectedAfterQuote | pos: usize, byte: u8 | A quoted field ended (closing |
UnterminatedQuote | start: usize | The input ended inside a quoted field (no closing |
BareCarriageReturn | pos: usize | A bare CR that is not followed by LF. |
UnevenRecord | record: usize, expected: usize, found: usize | A record has a different number of fields than the first one (only
raised by |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
Methods
to_string : (CsvError) fn(self : CsvError) -> Stringsource : (CsvError) fn(self : CsvError) -> Option(dyn( + ToString))The error that caused this one, or .None at the root of the chain.
Rust's Error::source. Defaulted to .None, so an error with nothing
underneath it implements the trait by saying only what it is; a wrapper
overrides it to hand back what it wrapped. Walking the chain to the root
cause is not expressible yet — the returned Dyn loses the Error
trait on an erased receiver, so a caller can print one link but cannot
follow it (#521,
issues/self-trait-in-a-return-type-loses-the-trait-on-an-erased-receiver.md).
Parameters
| Name | Type | Notes |
|---|---|---|
self | CsvError |
The record separator the writer emits.
Variants
| Variant | Fields | Description |
|---|---|---|
Lf |
| |
CrLf |
|
Reader/writer settings. default() is RFC 4180 with LF output:
comma delimiter, " quote, LF line ending.
Fields
| Name | Type | Description |
|---|---|---|
delimiter | u8 | Field delimiter byte ( |
line_ending | LineEnding | Line ending the WRITER emits; the reader always accepts both. |
impl(CsvOptions, ...)
default : (CsvOptions) fn() -> CsvOptionsRFC 4180 with LF output: , delimiter, \n line ending.
The line ending is the one deliberate divergence from the RFC, which
specifies CRLF. LF is what every Unix tool and every csv crate default
writes, and the reader accepts both either way, so a CRLF default would
only add carriage returns nobody asked for. Pass
CsvOptions(delimiter : u8(44), line_ending : LineEnding.CrLf) when the
consumer really wants RFC-literal output.
Returns: CsvOptions
with_delimiter : (CsvOptions) fn(delimiter : u8) -> CsvOptionsdefault() with another delimiter.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
delimiter | u8 | Field delimiter byte ( |
Returns: CsvOptions
Functions
Parse input with opts. Records are returned in order; an empty input
yields zero records, and a trailing terminator does not add an empty
record. Fields are never trimmed. Records may have different lengths —
use parse_strict to reject that.
Parameters
| Name | Type | Notes |
|---|---|---|
input | String | |
opts | CsvOptions |
Like parse_with, and additionally requires every record to have as
many fields as the first one (CsvError.UnevenRecord otherwise).
Parameters
| Name | Type | Notes |
|---|---|---|
input | String | |
opts | CsvOptions |
Serialize records with opts. Every record ends with the configured
line ending, including the last (RFC 4180 allows either; a terminated
last record concatenates cleanly).
Parameters
| Name | Type | Notes |
|---|---|---|
records | ArrayList(ArrayList(String)) | |
opts | CsvOptions |
Returns: String
DEPRECATED, removed in v0.2.32: call csv.write_with.
Parameters
| Name | Type | Notes |
|---|---|---|
records | ArrayList(ArrayList(String)) | |
opts | CsvOptions |
Returns: String