#[cfg(feature = "postgres")]
use super::block_on_postgres;
use super::{Commerce, CommerceBackend};
use std::sync::Arc;
use stateset_core::CommerceError;
use stateset_db::{Database, DatabaseConfig};
use stateset_observability::{MetricsConfig, init_metrics};
#[cfg(feature = "events")]
use crate::events::{EventConfig, EventSystem};
#[cfg(feature = "sqlite")]
use stateset_db::SqliteDatabase;
#[cfg(feature = "postgres")]
use stateset_db::PostgresDatabase;
#[derive(Default)]
#[must_use]
pub struct CommerceBuilder {
sqlite_path: Option<String>,
#[cfg(feature = "postgres")]
postgres_url: Option<String>,
max_connections: Option<u32>,
#[cfg(feature = "postgres")]
acquire_timeout_secs: Option<u64>,
#[cfg(feature = "events")]
event_config: Option<EventConfig>,
metrics_config: MetricsConfig,
}
impl std::fmt::Debug for CommerceBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommerceBuilder").finish_non_exhaustive()
}
}
impl CommerceBuilder {
pub fn new() -> Self {
Self::default()
}
#[cfg(feature = "sqlite")]
pub fn sqlite(mut self, path: &str) -> Self {
self.sqlite_path = Some(path.to_string());
self
}
#[cfg(feature = "sqlite")]
pub fn database(self, path: &str) -> Self {
self.sqlite(path)
}
#[cfg(feature = "sqlite")]
pub fn in_memory(mut self) -> Self {
self.sqlite_path = Some(":memory:".to_string());
self
}
#[cfg(feature = "postgres")]
pub fn postgres(mut self, url: &str) -> Self {
self.postgres_url = Some(url.to_string());
self
}
pub const fn max_connections(mut self, count: u32) -> Self {
self.max_connections = Some(count);
self
}
pub const fn metrics_config(mut self, config: MetricsConfig) -> Self {
self.metrics_config = config;
self
}
pub const fn disable_metrics(mut self) -> Self {
self.metrics_config.enabled = false;
self
}
#[cfg(feature = "postgres")]
pub fn acquire_timeout_secs(mut self, secs: u64) -> Self {
self.acquire_timeout_secs = Some(secs);
self
}
#[cfg(feature = "events")]
pub fn event_config(mut self, config: EventConfig) -> Self {
self.event_config = Some(config);
self
}
#[cfg(feature = "sqlite")]
pub fn build_with_defaults(self) -> Result<Commerce, CommerceError> {
self.in_memory().build()
}
pub fn build(self) -> Result<Commerce, CommerceError> {
let metrics = init_metrics(self.metrics_config.clone());
#[cfg(feature = "events")]
let event_system =
Arc::new(self.event_config.map(EventSystem::with_config).unwrap_or_default());
#[cfg(feature = "postgres")]
if let Some(url) = self.postgres_url {
let max_connections = self.max_connections.unwrap_or(10);
let acquire_timeout_secs = self.acquire_timeout_secs.unwrap_or(30);
let db = block_on_postgres(move || async move {
PostgresDatabase::connect_with_options(&url, max_connections, acquire_timeout_secs)
.await
})?;
let db: Arc<dyn Database> = Arc::new(db);
return Ok(Commerce {
db,
backend: CommerceBackend::Postgres,
metrics,
#[cfg(feature = "events")]
event_system,
#[cfg(all(feature = "sqlite", feature = "vector"))]
sqlite_db: None,
});
}
#[cfg(feature = "sqlite")]
{
let path = self.sqlite_path.unwrap_or_else(|| "stateset.db".to_string());
let mut config = if path == ":memory:" {
DatabaseConfig::in_memory()
} else {
DatabaseConfig::sqlite(&path)
};
if let Some(max) = self.max_connections {
config.max_connections = max;
}
let sqlite_db = Arc::new(SqliteDatabase::new(&config)?);
#[cfg(all(feature = "sqlite", feature = "vector"))]
let db: Arc<dyn Database> = sqlite_db.clone();
#[cfg(not(all(feature = "sqlite", feature = "vector")))]
let db: Arc<dyn Database> = sqlite_db;
Ok(Commerce {
db,
backend: CommerceBackend::Sqlite,
metrics,
#[cfg(feature = "events")]
event_system,
#[cfg(all(feature = "sqlite", feature = "vector"))]
sqlite_db: Some(sqlite_db),
})
}
#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
Err(CommerceError::Internal(
"No database backend enabled. Enable 'sqlite' or 'postgres' feature.".to_string(),
))
}
}