Module encoding/csv

encoding/csv
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()`. — stable modules only change additively; this one may still change.

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

CsvError enum
CsvError

A CSV decoding failure. Positions are byte offsets into the input.

Variants

VariantFieldsDescription
UnexpectedAfterQuotepos: usize, byte: u8

A quoted field ended (closing ") but was followed by something other than the delimiter, a record terminator, or the end of input — e.g. "ab"c. pos is the offending byte.

UnterminatedQuotestart: usize

The input ended inside a quoted field (no closing "). start is the byte of the opening quote.

BareCarriageReturnpos: usize

A bare CR that is not followed by LF. pos is the CR.

UnevenRecordrecord: usize, expected: usize, found: usize

A record has a different number of fields than the first one (only raised by parse_strict). record is the 0-based record index.

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) -> String

Render self under spec. An unrecognised spec degrades to the plain to_string() rendering rather than failing.

Parameters

NameTypeNotes
selfSelf
specstr

Returns: String

Methods
to_string : (CsvError) fn(self : CsvError) -> String

Parameters

NameTypeNotes
selfCsvError

Returns: String

source : (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

NameTypeNotes
selfCsvError

Returns: Option(dyn( + ToString))

LineEnding enum
LineEnding

The record separator the writer emits.

Variants

VariantFieldsDescription
Lf

\n

CrLf

\r\n

CsvOptions struct
CsvOptions

Reader/writer settings. default() is RFC 4180 with LF output: comma delimiter, " quote, LF line ending.

Fields

NameTypeDescription
delimiteru8

Field delimiter byte (, by default; \t for TSV, ; for locales that use it).

line_endingLineEnding

Line ending the WRITER emits; the reader always accepts both.

impl(CsvOptions, ...)
default : (CsvOptions) fn() -> CsvOptions

RFC 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) -> CsvOptions

default() with another delimiter.

Parameters

NameTypeNotesDescription
delimiteru8

Field delimiter byte (, by default; \t for TSV, ; for locales that use it).

Returns: CsvOptions

Functions

parse function
fn(input : String) -> Result(ArrayList(ArrayList(String)), CsvError)

parse_with(input, CsvOptions.default()).

Parameters

NameTypeNotes
inputString

Returns: Result(ArrayList(ArrayList(String)), CsvError)

parse_with function
fn(input : String, opts : CsvOptions) -> Result(ArrayList(ArrayList(String)), CsvError)

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

NameTypeNotes
inputString
optsCsvOptions

Returns: Result(ArrayList(ArrayList(String)), CsvError)

parse_strict function
fn(input : String, opts : CsvOptions) -> Result(ArrayList(ArrayList(String)), CsvError)

Like parse_with, and additionally requires every record to have as many fields as the first one (CsvError.UnevenRecord otherwise).

Parameters

NameTypeNotes
inputString
optsCsvOptions

Returns: Result(ArrayList(ArrayList(String)), CsvError)

write function
fn(records : ArrayList(ArrayList(String))) -> String

write_with(records, CsvOptions.default()).

Parameters

NameTypeNotes
recordsArrayList(ArrayList(String))

Returns: String

write_with function
fn(records : ArrayList(ArrayList(String)), opts : CsvOptions) -> String

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

NameTypeNotes
recordsArrayList(ArrayList(String))
optsCsvOptions

Returns: String

csv_parse function
fn(input : String) -> Result(ArrayList(ArrayList(String)), CsvError)

DEPRECATED, removed in v0.2.32: call csv.parse.

Parameters

NameTypeNotes
inputString

Returns: Result(ArrayList(ArrayList(String)), CsvError)

csv_parse_with function
fn(input : String, opts : CsvOptions) -> Result(ArrayList(ArrayList(String)), CsvError)

DEPRECATED, removed in v0.2.32: call csv.parse_with.

Parameters

NameTypeNotes
inputString
optsCsvOptions

Returns: Result(ArrayList(ArrayList(String)), CsvError)

csv_parse_strict function
fn(input : String, opts : CsvOptions) -> Result(ArrayList(ArrayList(String)), CsvError)

DEPRECATED, removed in v0.2.32: call csv.parse_strict.

Parameters

NameTypeNotes
inputString
optsCsvOptions

Returns: Result(ArrayList(ArrayList(String)), CsvError)

csv_write function
fn(records : ArrayList(ArrayList(String))) -> String

DEPRECATED, removed in v0.2.32: call csv.write.

Parameters

NameTypeNotes
recordsArrayList(ArrayList(String))

Returns: String

csv_write_with function
fn(records : ArrayList(ArrayList(String)), opts : CsvOptions) -> String

DEPRECATED, removed in v0.2.32: call csv.write_with.

Parameters

NameTypeNotes
recordsArrayList(ArrayList(String))
optsCsvOptions

Returns: String