Skip to main content

feldera_types/format/
csv.rs

1use std::fmt::Debug;
2
3use serde::{Deserialize, Serialize};
4use utoipa::ToSchema;
5
6/// Whitespace trimming policy applied to CSV fields or header names.
7#[derive(Clone, Debug, Default, Deserialize, Serialize, ToSchema, PartialEq, Eq)]
8#[serde(rename_all = "snake_case")]
9pub enum CsvTrim {
10    /// Do not trim whitespace (default).
11    #[default]
12    None,
13    /// Trim whitespace from header names only.
14    Headers,
15    /// Trim whitespace from field values only.
16    Fields,
17    /// Trim whitespace from both header names and field values.
18    All,
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize, ToSchema, PartialEq)]
22#[serde(default)]
23pub struct CsvFormatConfig {
24    /// Field delimiter (default `','`).
25    ///
26    /// Must be an ASCII character.
27    ///
28    /// Used by: input and output.
29    pub delimiter: char,
30
31    /// Whether the input begins with a header line (which is skipped).
32    ///
33    /// Used by: input only.
34    pub headers: bool,
35
36    /// The quote character (default `'"'`).
37    ///
38    /// Must be an ASCII character.  Set `quoting` to `false` to disable
39    /// quoting entirely.
40    ///
41    /// Used by: input only.
42    pub quote: char,
43
44    /// The escape character for quoted fields (default: `None`).
45    ///
46    /// When `None` (the default), the CSV parser uses the double-quote
47    /// convention: a literal quote inside a quoted field is written as two
48    /// consecutive quote characters.  When set, the given character is used
49    /// as the escape prefix instead (e.g. `Some('\\')` for backslash
50    /// escaping).
51    ///
52    /// Must be an ASCII character.
53    ///
54    /// Used by: input only.
55    pub escape: Option<char>,
56
57    /// Enable double-quote escaping (default `true`).
58    ///
59    /// When `true`, a quote character inside a quoted field may be escaped by
60    /// doubling it.  Setting this to `false` disables double-quote escaping
61    /// (an explicit `escape` character can still be used).
62    ///
63    /// Used by: input only.
64    pub double_quote: bool,
65
66    /// Enable quoting (default `true`).
67    ///
68    /// When `false`, the `quote` and `escape` characters have no special
69    /// meaning and every newline terminates a record regardless of context.
70    ///
71    /// Used by: input only.
72    pub quoting: bool,
73
74    /// Comment character (default: `None`).
75    ///
76    /// When set, lines whose first byte matches this character are treated as
77    /// comments and skipped entirely.  Must be an ASCII character.
78    ///
79    /// Used by: input only.
80    pub comment: Option<char>,
81
82    /// Allow records with a variable number of fields (default `true`).
83    ///
84    /// When `true`, records that have fewer or more fields than expected are
85    /// accepted rather than treated as errors.
86    ///
87    /// Used by: input only.
88    pub flexible: bool,
89
90    /// Whitespace trimming policy (default [`CsvTrim::None`]).
91    ///
92    /// Used by: input only.
93    pub trim: CsvTrim,
94}
95
96impl CsvFormatConfig {
97    pub fn delimiter(&self) -> CsvDelimiter {
98        self.delimiter.into()
99    }
100}
101
102impl Default for CsvFormatConfig {
103    fn default() -> Self {
104        Self {
105            delimiter: CsvDelimiter::default().0.into(),
106            headers: false,
107            quote: '"',
108            escape: None,
109            double_quote: true,
110            quoting: true,
111            comment: None,
112            flexible: true,
113            trim: CsvTrim::None,
114        }
115    }
116}
117
118/// A delimiter between CSV records, typically `b','`.
119#[derive(Copy, Clone)]
120pub struct CsvDelimiter(pub u8);
121
122impl CsvDelimiter {
123    pub const DEFAULT: CsvDelimiter = CsvDelimiter(b',');
124}
125
126impl Default for CsvDelimiter {
127    fn default() -> Self {
128        Self::DEFAULT
129    }
130}
131
132impl Debug for CsvDelimiter {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        f.debug_tuple("CsvDelimiter")
135            .field(&char::from(self.0))
136            .finish()
137    }
138}
139
140impl From<char> for CsvDelimiter {
141    fn from(value: char) -> Self {
142        Self(value.try_into().unwrap_or(b','))
143    }
144}
145
146#[derive(Debug, Deserialize, Serialize, ToSchema)]
147#[serde(default)]
148pub struct CsvEncoderConfig {
149    /// Field delimiter (default `','`).
150    ///
151    /// This must be an ASCII character.
152    pub delimiter: char,
153
154    pub buffer_size_records: usize,
155}
156
157impl Default for CsvEncoderConfig {
158    fn default() -> Self {
159        Self {
160            delimiter: CsvDelimiter::default().0.into(),
161            buffer_size_records: 10_000,
162        }
163    }
164}