tabnas_alchemy/shared/csv.rs
1//! The CSV dialect: the options render's `CsvRenderer` takes, which
2//! alchemy builds from a program's `csv-options` and hands to
3//! [`Renderers::csv`](crate::shared::Renderers::csv).
4//!
5//! The standard profile quotes every field, doubles `"`, ends every record
6//! with CRLF and writes numbers as their lexemes; minimal quoting and other
7//! delimiters are dialects the caller selects explicitly.
8
9/// The record terminator.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum Newline {
12 Lf,
13 /// RFC 4180's terminator, and the standard profile's.
14 #[default]
15 CrLf,
16}
17
18impl Newline {
19 pub fn as_str(self) -> &'static str {
20 match self {
21 Newline::Lf => "\n",
22 Newline::CrLf => "\r\n",
23 }
24 }
25}
26
27/// When a field is quoted.
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
29pub enum Quoting {
30 /// Every field, the standard profile.
31 #[default]
32 Always,
33 /// Only a field holding the delimiter, `"`, CR or LF. An empty field is
34 /// then written as nothing, so the empty string and an empty null text
35 /// read back the same; that is the dialect's trade-off, not a defect.
36 Minimal,
37}
38
39/// What a [`Cell::Missing`](crate::shared::Cell::Missing) becomes.
40#[derive(Clone, Debug, Default, PartialEq, Eq)]
41pub enum MissingText {
42 /// Fail the run with `MISSING_VALUE`: a table that promised a column
43 /// and did not deliver it is not silently padded.
44 #[default]
45 Error,
46 /// Write this text instead.
47 Text(Box<str>),
48}
49
50/// The CSV dialect.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct CsvOptions {
53 /// One character, and not `"`, CR, LF or NUL: those would make the
54 /// output unreadable by construction, and are refused when the renderer
55 /// is built.
56 pub delimiter: char,
57 pub newline: Newline,
58 /// Write the labels as the first record.
59 pub header: bool,
60 /// The text of a [`Cell::Null`](crate::shared::Cell::Null); empty by default.
61 pub null_text: Box<str>,
62 pub missing: MissingText,
63 pub quoting: Quoting,
64}
65
66impl Default for CsvOptions {
67 fn default() -> Self {
68 CsvOptions {
69 delimiter: ',',
70 newline: Newline::CrLf,
71 header: true,
72 null_text: "".into(),
73 missing: MissingText::Error,
74 quoting: Quoting::Always,
75 }
76 }
77}