use anyhow::Result;
use drasi_lib::bootstrap::BootstrapProvider;
use drasi_lib::channels::DispatchMode;
use drasi_lib::sources::base::SourceBaseParams;
use crate::SqliteSource;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TableKeyConfig {
pub table: String,
pub key_columns: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RestApiConfig {
pub host: String,
pub port: u16,
}
impl Default for RestApiConfig {
fn default() -> Self {
Self {
host: default_rest_host(),
port: default_rest_port(),
}
}
}
fn default_rest_host() -> String {
"127.0.0.1".to_string()
}
fn default_rest_port() -> u16 {
8080
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum StartFrom {
#[default]
Beginning,
Now,
Timestamp(i64),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SqliteSourceConfig {
pub path: Option<String>,
pub tables: Option<Vec<String>>,
pub table_keys: Vec<TableKeyConfig>,
pub rest_api: Option<RestApiConfig>,
pub start_from: StartFrom,
}
impl Default for SqliteSourceConfig {
fn default() -> Self {
Self {
path: None,
tables: None,
table_keys: Vec::new(),
rest_api: None,
start_from: StartFrom::Beginning,
}
}
}
pub struct SqliteSourceBuilder {
pub(crate) id: String,
path: Option<String>,
tables: Option<Vec<String>>,
table_keys: Vec<TableKeyConfig>,
rest_api: Option<RestApiConfig>,
start_from: StartFrom,
dispatch_mode: Option<DispatchMode>,
dispatch_buffer_capacity: Option<usize>,
bootstrap_provider: Option<Box<dyn BootstrapProvider + 'static>>,
auto_start: bool,
}
impl SqliteSourceBuilder {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
path: None,
tables: None,
table_keys: Vec::new(),
rest_api: None,
start_from: StartFrom::Beginning,
dispatch_mode: None,
dispatch_buffer_capacity: None,
bootstrap_provider: None,
auto_start: true,
}
}
pub fn with_path(mut self, path: impl Into<String>) -> Self {
self.path = Some(path.into());
self
}
pub fn in_memory(mut self) -> Self {
self.path = None;
self
}
pub fn with_tables(mut self, tables: Vec<String>) -> Self {
self.tables = Some(tables);
self
}
pub fn with_table_keys(mut self, table_keys: Vec<TableKeyConfig>) -> Self {
self.table_keys = table_keys;
self
}
pub fn with_rest_api(mut self, config: RestApiConfig) -> Self {
self.rest_api = Some(config);
self
}
pub fn start_from(mut self, start_from: StartFrom) -> Self {
self.start_from = start_from;
self
}
pub fn with_dispatch_mode(mut self, mode: DispatchMode) -> Self {
self.dispatch_mode = Some(mode);
self
}
pub fn with_dispatch_buffer_capacity(mut self, capacity: usize) -> Self {
self.dispatch_buffer_capacity = Some(capacity);
self
}
pub fn with_bootstrap_provider(mut self, provider: impl BootstrapProvider + 'static) -> Self {
self.bootstrap_provider = Some(Box::new(provider));
self
}
pub fn auto_start(mut self, auto_start: bool) -> Self {
self.auto_start = auto_start;
self
}
pub fn build(self) -> Result<SqliteSource> {
let config = SqliteSourceConfig {
path: self.path,
tables: self.tables,
table_keys: self.table_keys,
rest_api: self.rest_api,
start_from: self.start_from,
};
let mut params = SourceBaseParams::new(self.id);
if let Some(mode) = self.dispatch_mode {
params = params.with_dispatch_mode(mode);
}
if let Some(capacity) = self.dispatch_buffer_capacity {
params = params.with_dispatch_buffer_capacity(capacity);
}
if let Some(provider) = self.bootstrap_provider {
params = params.with_bootstrap_provider(provider);
}
params = params.with_auto_start(self.auto_start);
let base = drasi_lib::sources::base::SourceBase::new(params)?;
SqliteSource::from_parts(base, config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use drasi_lib::Source;
#[test]
fn sqlite_source_config_defaults_are_stable() {
let config = SqliteSourceConfig::default();
assert!(config.path.is_none());
assert!(config.tables.is_none());
assert!(config.table_keys.is_empty());
assert!(config.rest_api.is_none());
assert_eq!(config.start_from, StartFrom::Beginning);
}
#[test]
fn builder_applies_rest_and_dispatch_configuration() {
let source = SqliteSource::builder("test-source")
.with_path("/tmp/test.db")
.with_tables(vec!["sensors".to_string()])
.with_table_keys(vec![TableKeyConfig {
table: "sensors".to_string(),
key_columns: vec!["id".to_string()],
}])
.with_rest_api(RestApiConfig {
host: "127.0.0.1".to_string(), port: 9001,
})
.with_dispatch_mode(DispatchMode::Channel)
.with_dispatch_buffer_capacity(42)
.auto_start(false)
.build()
.expect("failed to build sqlite source");
let properties = source.properties();
assert_eq!(
properties.get("path"),
Some(&serde_json::json!("/tmp/test.db"))
);
assert_eq!(
properties.get("tables"),
Some(&serde_json::json!(["sensors"]))
);
assert!(properties.contains_key("restApi"));
assert!(!source.auto_start());
}
}