faucet_sink_sqlite/
config.rs1use faucet_core::DEFAULT_BATCH_SIZE;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "snake_case")]
10pub enum SqliteColumnMapping {
11 Json { column: String },
14 AutoMap,
17}
18
19impl Default for SqliteColumnMapping {
20 fn default() -> Self {
21 Self::Json {
22 column: "data".into(),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29pub struct SqliteSinkConfig {
30 pub database_url: String,
32 pub table_name: String,
34 pub column_mapping: SqliteColumnMapping,
36 #[serde(default = "default_batch_size")]
50 pub batch_size: usize,
51 #[serde(default = "default_max_connections")]
61 pub max_connections: u32,
62 #[serde(flatten)]
66 pub write: faucet_core::WriteSpec,
67}
68
69fn default_batch_size() -> usize {
70 DEFAULT_BATCH_SIZE
71}
72
73fn default_max_connections() -> u32 {
74 1
75}
76
77impl SqliteSinkConfig {
78 pub fn new(database_url: impl Into<String>, table_name: impl Into<String>) -> Self {
80 Self {
81 database_url: database_url.into(),
82 table_name: table_name.into(),
83 column_mapping: SqliteColumnMapping::default(),
84 batch_size: DEFAULT_BATCH_SIZE,
85 max_connections: default_max_connections(),
86 write: faucet_core::WriteSpec::default(),
87 }
88 }
89
90 pub fn column_mapping(mut self, mapping: SqliteColumnMapping) -> Self {
92 self.column_mapping = mapping;
93 self
94 }
95
96 pub fn with_batch_size(mut self, batch_size: usize) -> Self {
102 self.batch_size = batch_size;
103 self
104 }
105
106 pub fn max_connections(mut self, n: u32) -> Self {
108 self.max_connections = n;
109 self
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn default_config() {
119 let config = SqliteSinkConfig::new("sqlite::memory:", "events");
120 assert_eq!(config.table_name, "events");
121 assert_eq!(config.batch_size, DEFAULT_BATCH_SIZE);
122 assert!(matches!(
123 config.column_mapping,
124 SqliteColumnMapping::Json { ref column } if column == "data"
125 ));
126 }
127
128 #[test]
129 fn default_max_connections_is_one() {
130 let config = SqliteSinkConfig::new("sqlite::memory:", "events");
134 assert_eq!(config.max_connections, 1);
135 }
136
137 #[test]
138 fn default_max_connections_deserializes_to_one_when_absent() {
139 let json = r#"{
140 "database_url": "sqlite::memory:",
141 "table_name": "events",
142 "column_mapping": {"json": {"column": "data"}}
143 }"#;
144 let config: SqliteSinkConfig = serde_json::from_str(json).unwrap();
145 assert_eq!(config.max_connections, 1);
146 }
147
148 #[test]
149 fn builder_methods() {
150 let config = SqliteSinkConfig::new("sqlite::memory:", "events")
151 .column_mapping(SqliteColumnMapping::AutoMap)
152 .with_batch_size(100);
153 assert_eq!(config.batch_size, 100);
154 assert!(matches!(
155 config.column_mapping,
156 SqliteColumnMapping::AutoMap
157 ));
158 }
159
160 #[test]
161 fn json_custom_column() {
162 let config = SqliteSinkConfig::new("sqlite::memory:", "events").column_mapping(
163 SqliteColumnMapping::Json {
164 column: "payload".into(),
165 },
166 );
167 assert!(matches!(
168 config.column_mapping,
169 SqliteColumnMapping::Json { ref column } if column == "payload"
170 ));
171 }
172
173 #[test]
174 fn config_with_file_path() {
175 let config = SqliteSinkConfig::new("/tmp/test.db", "events");
176 assert_eq!(config.database_url, "/tmp/test.db");
177 }
178
179 #[test]
180 fn batch_size_defaults_to_default_batch_size() {
181 let config = SqliteSinkConfig::new("sqlite::memory:", "events");
182 assert_eq!(config.batch_size, faucet_core::DEFAULT_BATCH_SIZE);
183 }
184
185 #[test]
186 fn with_batch_size_overrides_default() {
187 let config = SqliteSinkConfig::new("sqlite::memory:", "events").with_batch_size(250);
188 assert_eq!(config.batch_size, 250);
189 }
190
191 #[test]
192 fn batch_size_zero_is_accepted_as_no_batching_sentinel() {
193 let config = SqliteSinkConfig::new("sqlite::memory:", "events").with_batch_size(0);
194 assert_eq!(config.batch_size, 0);
195 assert!(faucet_core::validate_batch_size(config.batch_size).is_ok());
196 }
197
198 #[test]
199 fn batch_size_above_max_is_rejected_by_validate_batch_size() {
200 let config = SqliteSinkConfig::new("sqlite::memory:", "events")
201 .with_batch_size(faucet_core::MAX_BATCH_SIZE + 1);
202 assert!(faucet_core::validate_batch_size(config.batch_size).is_err());
203 }
204
205 #[test]
206 fn batch_size_deserializes_from_json() {
207 let json = r#"{
208 "database_url": "sqlite::memory:",
209 "table_name": "events",
210 "column_mapping": {"json": {"column": "data"}},
211 "batch_size": 250,
212 "max_connections": 5
213 }"#;
214 let config: SqliteSinkConfig = serde_json::from_str(json).unwrap();
215 assert_eq!(config.batch_size, 250);
216 }
217
218 #[test]
219 fn batch_size_defaults_when_absent_in_json() {
220 let json = r#"{
221 "database_url": "sqlite::memory:",
222 "table_name": "events",
223 "column_mapping": {"json": {"column": "data"}},
224 "max_connections": 5
225 }"#;
226 let config: SqliteSinkConfig = serde_json::from_str(json).unwrap();
227 assert_eq!(config.batch_size, faucet_core::DEFAULT_BATCH_SIZE);
228 }
229}