1use std::fmt::Debug;
8
9use async_trait::async_trait;
10
11use crate::error::BackendError;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum BackendKind {
19 Sqlite,
21 Postgres,
23 Cassandra,
25 MongoDB,
27 Neo4j,
29 Elasticsearch,
31 S3,
33 Custom(&'static str),
35}
36
37impl std::fmt::Display for BackendKind {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 BackendKind::Sqlite => write!(f, "sqlite"),
41 BackendKind::Postgres => write!(f, "postgres"),
42 BackendKind::Cassandra => write!(f, "cassandra"),
43 BackendKind::MongoDB => write!(f, "mongodb"),
44 BackendKind::Neo4j => write!(f, "neo4j"),
45 BackendKind::Elasticsearch => write!(f, "elasticsearch"),
46 BackendKind::S3 => write!(f, "s3"),
47 BackendKind::Custom(name) => write!(f, "{}", name),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
56pub enum BackendCapability {
57 Crud,
59 Versioning,
61 InstanceHistory,
63 TypeHistory,
65 SystemHistory,
67 BasicSearch,
69 DateSearch,
71 QuantitySearch,
73 ReferenceSearch,
75 ChainedSearch,
77 ReverseChaining,
79 Include,
81 Revinclude,
83 FullTextSearch,
85 TerminologySearch,
87 Transactions,
89 OptimisticLocking,
91 PessimisticLocking,
93 CursorPagination,
95 OffsetPagination,
97 Sorting,
99 BulkExport,
101 BulkSubmitIngest,
103 BulkSubmitRestWorker,
105 SharedSchema,
107 SchemaPerTenant,
109 DatabasePerTenant,
111 InDbSofRunner,
113 RawSqlQuery,
115}
116
117impl std::fmt::Display for BackendCapability {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 let name = match self {
120 BackendCapability::Crud => "crud",
121 BackendCapability::Versioning => "versioning",
122 BackendCapability::InstanceHistory => "instance-history",
123 BackendCapability::TypeHistory => "type-history",
124 BackendCapability::SystemHistory => "system-history",
125 BackendCapability::BasicSearch => "basic-search",
126 BackendCapability::DateSearch => "date-search",
127 BackendCapability::QuantitySearch => "quantity-search",
128 BackendCapability::ReferenceSearch => "reference-search",
129 BackendCapability::ChainedSearch => "chained-search",
130 BackendCapability::ReverseChaining => "reverse-chaining",
131 BackendCapability::Include => "include",
132 BackendCapability::Revinclude => "revinclude",
133 BackendCapability::FullTextSearch => "full-text-search",
134 BackendCapability::TerminologySearch => "terminology-search",
135 BackendCapability::Transactions => "transactions",
136 BackendCapability::OptimisticLocking => "optimistic-locking",
137 BackendCapability::PessimisticLocking => "pessimistic-locking",
138 BackendCapability::CursorPagination => "cursor-pagination",
139 BackendCapability::OffsetPagination => "offset-pagination",
140 BackendCapability::Sorting => "sorting",
141 BackendCapability::BulkExport => "bulk-export",
142 BackendCapability::BulkSubmitIngest => "bulk-submit-ingest",
143 BackendCapability::BulkSubmitRestWorker => "bulk-submit-rest-worker",
144 BackendCapability::SharedSchema => "shared-schema",
145 BackendCapability::SchemaPerTenant => "schema-per-tenant",
146 BackendCapability::DatabasePerTenant => "database-per-tenant",
147 BackendCapability::InDbSofRunner => "indb-sof-runner",
148 BackendCapability::RawSqlQuery => "raw-sql-query",
149 };
150 write!(f, "{}", name)
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct BackendConfig {
157 pub connection_string: String,
159 pub max_connections: u32,
161 pub min_connections: u32,
163 pub connect_timeout_ms: u64,
165 pub idle_timeout_ms: Option<u64>,
167 pub max_lifetime_ms: Option<u64>,
169}
170
171impl Default for BackendConfig {
172 fn default() -> Self {
173 Self {
174 connection_string: String::new(),
175 max_connections: 10,
176 min_connections: 1,
177 connect_timeout_ms: 5000,
178 idle_timeout_ms: Some(600_000), max_lifetime_ms: Some(1_800_000), }
181 }
182}
183
184impl BackendConfig {
185 pub fn new(connection_string: impl Into<String>) -> Self {
187 Self {
188 connection_string: connection_string.into(),
189 ..Default::default()
190 }
191 }
192
193 pub fn with_max_connections(mut self, max: u32) -> Self {
195 self.max_connections = max;
196 self
197 }
198
199 pub fn with_connect_timeout_ms(mut self, timeout: u64) -> Self {
201 self.connect_timeout_ms = timeout;
202 self
203 }
204}
205
206#[async_trait]
231pub trait Backend: Send + Sync + Debug {
232 type Connection: Send;
234
235 fn kind(&self) -> BackendKind;
237
238 fn name(&self) -> &'static str;
240
241 fn supports(&self, capability: BackendCapability) -> bool;
243
244 fn capabilities(&self) -> Vec<BackendCapability>;
246
247 async fn acquire(&self) -> Result<Self::Connection, BackendError>;
249
250 async fn release(&self, conn: Self::Connection);
252
253 async fn health_check(&self) -> Result<(), BackendError>;
255
256 async fn initialize(&self) -> Result<(), BackendError>;
258
259 async fn migrate(&self) -> Result<(), BackendError>;
261}
262
263pub trait BackendPoolStats {
265 fn active_connections(&self) -> u32;
267
268 fn idle_connections(&self) -> u32;
270
271 fn max_connections(&self) -> u32;
273
274 fn pending_connections(&self) -> u32;
276}
277
278pub trait TransactionalBackend: Backend {}
280
281pub trait FullTextBackend: Backend {}
283
284pub trait GraphBackend: Backend {}
286
287pub trait TimeSeriesBackend: Backend {}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_backend_kind_display() {
296 assert_eq!(BackendKind::Sqlite.to_string(), "sqlite");
297 assert_eq!(BackendKind::Postgres.to_string(), "postgres");
298 assert_eq!(BackendKind::Custom("custom-db").to_string(), "custom-db");
299 }
300
301 #[test]
302 fn test_backend_capability_display() {
303 assert_eq!(BackendCapability::Crud.to_string(), "crud");
304 assert_eq!(
305 BackendCapability::ChainedSearch.to_string(),
306 "chained-search"
307 );
308 assert_eq!(
309 BackendCapability::FullTextSearch.to_string(),
310 "full-text-search"
311 );
312 assert_eq!(
313 BackendCapability::BulkSubmitIngest.to_string(),
314 "bulk-submit-ingest"
315 );
316 assert_eq!(
317 BackendCapability::BulkSubmitRestWorker.to_string(),
318 "bulk-submit-rest-worker"
319 );
320 }
321
322 #[test]
323 fn test_backend_config_default() {
324 let config = BackendConfig::default();
325 assert_eq!(config.max_connections, 10);
326 assert_eq!(config.min_connections, 1);
327 assert_eq!(config.connect_timeout_ms, 5000);
328 }
329
330 #[test]
331 fn test_backend_config_builder() {
332 let config = BackendConfig::new("postgres://localhost/db")
333 .with_max_connections(20)
334 .with_connect_timeout_ms(10000);
335
336 assert_eq!(config.connection_string, "postgres://localhost/db");
337 assert_eq!(config.max_connections, 20);
338 assert_eq!(config.connect_timeout_ms, 10000);
339 }
340}