Skip to main content

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}