use faucet_common_delta::DeltaConnection;
use faucet_core::{DEFAULT_BATCH_SIZE, validate_batch_size};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
fn default_batch_size() -> usize {
DEFAULT_BATCH_SIZE
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DeltaSourceConfig {
#[serde(flatten)]
pub connection: DeltaConnection,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timestamp: Option<String>,
#[serde(default)]
pub columns: Vec<String>,
#[serde(default = "default_batch_size")]
pub batch_size: usize,
}
impl DeltaSourceConfig {
pub fn new(table_uri: impl Into<String>) -> Self {
Self {
connection: DeltaConnection {
table_uri: table_uri.into(),
credentials: Default::default(),
storage_options: Default::default(),
},
version: None,
timestamp: None,
columns: Vec::new(),
batch_size: DEFAULT_BATCH_SIZE,
}
}
pub fn validate(&self) -> Result<(), String> {
if self.connection.table_uri.trim().is_empty() {
return Err("delta source: `table_uri` must not be empty".to_string());
}
if self.version.is_some() && self.timestamp.is_some() {
return Err(
"delta source: `version` and `timestamp` are mutually exclusive".to_string(),
);
}
validate_batch_size(self.batch_size)
.map_err(|e| format!("delta source: invalid batch_size: {e}"))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn new_has_sane_defaults() {
let c = DeltaSourceConfig::new("file:///tmp/t");
assert_eq!(c.connection.table_uri, "file:///tmp/t");
assert_eq!(c.batch_size, DEFAULT_BATCH_SIZE);
assert!(c.columns.is_empty());
c.validate().unwrap();
}
#[test]
fn rejects_empty_uri() {
let mut c = DeltaSourceConfig::new("");
assert!(c.validate().is_err());
c.connection.table_uri = " ".into();
assert!(c.validate().is_err());
}
#[test]
fn rejects_version_and_timestamp_together() {
let mut c = DeltaSourceConfig::new("file:///tmp/t");
c.version = Some(3);
c.timestamp = Some("2026-01-01T00:00:00Z".into());
assert!(c.validate().is_err());
}
#[test]
fn rejects_oversized_batch() {
let mut c = DeltaSourceConfig::new("file:///tmp/t");
c.batch_size = faucet_core::MAX_BATCH_SIZE + 1;
assert!(c.validate().is_err());
}
#[test]
fn deserializes_flattened_shape() {
let v = json!({
"table_uri": "s3://b/t",
"credentials": { "type": "aws", "config": { "region": "us-east-1" } },
"columns": ["id", "name"],
"version": 5
});
let c: DeltaSourceConfig = serde_json::from_value(v).unwrap();
assert_eq!(c.connection.table_uri, "s3://b/t");
assert_eq!(c.columns, vec!["id", "name"]);
assert_eq!(c.version, Some(5));
c.validate().unwrap();
}
}