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}