rskit_database/
registry.rs1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use rskit_errors::{AppError, AppResult, ErrorCode};
7
8use crate::config::DatabaseConfig;
9use crate::database::{DatabaseClient, memory_from_config};
10
11#[async_trait::async_trait]
13pub trait DatabaseFactory: Send + Sync {
14 async fn create(&self, config: &DatabaseConfig) -> AppResult<Arc<dyn DatabaseClient>>;
16}
17
18#[derive(Default)]
20pub struct DatabaseRegistry {
21 factories: BTreeMap<String, Arc<dyn DatabaseFactory>>,
22}
23
24impl DatabaseRegistry {
25 #[must_use]
27 pub fn new() -> Self {
28 Self::default()
29 }
30
31 pub fn register(
33 &mut self,
34 name: impl Into<String>,
35 factory: Arc<dyn DatabaseFactory>,
36 ) -> AppResult<()> {
37 let name = name.into().trim().to_owned();
38 if name.is_empty() {
39 return Err(AppError::new(
40 ErrorCode::InvalidInput,
41 "database backend name is required",
42 ));
43 }
44 if self.factories.contains_key(&name) {
45 return Err(AppError::new(
46 ErrorCode::AlreadyExists,
47 format!("database backend '{name}' is already registered"),
48 ));
49 }
50 self.factories.insert(name, factory);
51 Ok(())
52 }
53
54 #[must_use]
56 pub fn contains(&self, name: &str) -> bool {
57 self.factories.contains_key(name)
58 }
59
60 #[must_use]
62 pub fn len(&self) -> usize {
63 self.factories.len()
64 }
65
66 #[must_use]
68 pub fn is_empty(&self) -> bool {
69 self.factories.is_empty()
70 }
71
72 pub async fn build(&self, config: &DatabaseConfig) -> AppResult<Arc<dyn DatabaseClient>> {
74 let backend = config.backend.trim();
75 if backend.is_empty() {
76 return Err(AppError::new(
77 ErrorCode::InvalidInput,
78 "database backend name is required",
79 ));
80 }
81 self.factories
82 .get(backend)
83 .ok_or_else(|| {
84 AppError::new(
85 ErrorCode::NotFound,
86 format!("database backend '{backend}' is not registered"),
87 )
88 })?
89 .create(config)
90 .await
91 }
92}
93
94struct MemoryFactory;
95
96#[async_trait::async_trait]
97impl DatabaseFactory for MemoryFactory {
98 async fn create(&self, config: &DatabaseConfig) -> AppResult<Arc<dyn DatabaseClient>> {
99 Ok(Arc::new(memory_from_config(config)))
100 }
101}
102
103pub fn register_memory(registry: &mut DatabaseRegistry) -> AppResult<()> {
105 registry.register("memory", Arc::new(MemoryFactory))
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[tokio::test]
113 async fn build_rejects_blank_backend() {
114 let registry = DatabaseRegistry::new();
115 let config = DatabaseConfig {
116 backend: " ".to_owned(),
117 ..DatabaseConfig::default()
118 };
119
120 let err = registry.build(&config).await.err().unwrap();
121
122 assert_eq!(err.code(), ErrorCode::InvalidInput);
123 }
124
125 #[tokio::test]
126 async fn build_normalizes_backend_before_lookup() {
127 let mut registry = DatabaseRegistry::new();
128 register_memory(&mut registry).unwrap();
129 let config = DatabaseConfig {
130 backend: " memory ".to_owned(),
131 ..DatabaseConfig::default()
132 };
133
134 let database = registry.build(&config).await.unwrap();
135
136 assert!(Arc::strong_count(&database) >= 1);
137 }
138}