Skip to main content

valence_core/
query_compiler_registry.rs

1//! Registry mapping [`DatabaseBackend::engine_id`](crate::backend::DatabaseBackend::engine_id) to query compilers.
2
3use std::collections::HashMap;
4use std::sync::{Arc, OnceLock};
5
6use crate::error::{Error, Result};
7use crate::known_engines::KnownEngines;
8use crate::query::QueryCore;
9use crate::query_compiler::QueryCompiler;
10use crate::CompiledQuery;
11
12/// Resolves a [`QueryCompiler`] for a storage engine slug.
13#[derive(Clone, Default)]
14pub struct QueryCompilerRegistry {
15    compilers: HashMap<&'static str, Arc<dyn QueryCompiler>>,
16}
17
18impl std::fmt::Debug for QueryCompilerRegistry {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        f.debug_struct("QueryCompilerRegistry")
21            .field("engines", &self.compilers.keys().collect::<Vec<_>>())
22            .finish()
23    }
24}
25
26impl QueryCompilerRegistry {
27    /// Build a registry from Cargo feature-enabled compilers.
28    #[must_use]
29    pub fn with_enabled_features() -> Self {
30        let mut registry = Self::default();
31        registry.register_builtins();
32        registry
33    }
34
35    fn register_builtins(&mut self) {
36        #[cfg(feature = "compiler-sql")]
37        {
38            let sql: Arc<dyn QueryCompiler> = Arc::new(crate::backend::SqlQueryCompiler);
39            self.register(KnownEngines::SQLITE, Arc::clone(&sql));
40            self.register(KnownEngines::POSTGRES, Arc::clone(&sql));
41            self.register(KnownEngines::INMEMORY_MEM, sql);
42        }
43        #[cfg(feature = "compiler-surreal")]
44        {
45            self.register(
46                KnownEngines::SURREALDB,
47                Arc::new(crate::backend::SurrealQueryCompiler) as Arc<dyn QueryCompiler>,
48            );
49        }
50        #[cfg(feature = "compiler-mongodb")]
51        {
52            self.register(
53                KnownEngines::MONGODB,
54                Arc::new(crate::backend::MongoQueryCompiler) as Arc<dyn QueryCompiler>,
55            );
56        }
57        #[cfg(feature = "compiler-redis")]
58        {
59            self.register(
60                KnownEngines::REDIS,
61                Arc::new(crate::backend::RedisQueryCompiler) as Arc<dyn QueryCompiler>,
62            );
63        }
64        #[cfg(feature = "compiler-indradb")]
65        {
66            self.register(
67                KnownEngines::INDRADB,
68                Arc::new(crate::backend::IndraQueryCompiler) as Arc<dyn QueryCompiler>,
69            );
70        }
71        #[cfg(feature = "compiler-hybrid")]
72        {
73            self.register(
74                KnownEngines::HYBRID_INDRA_SQL,
75                Arc::new(crate::backend::HybridQueryCompiler) as Arc<dyn QueryCompiler>,
76            );
77        }
78    }
79
80    /// Register a compiler for an open engine slug.
81    pub fn register(&mut self, engine_id: &'static str, compiler: Arc<dyn QueryCompiler>) {
82        self.compilers.insert(engine_id, compiler);
83    }
84
85    /// Look up a compiler by engine id.
86    pub fn get(&self, engine_id: &str) -> Option<&Arc<dyn QueryCompiler>> {
87        self.compilers.get(engine_id)
88    }
89
90    /// Compile `core` for `engine_id`, or return a clear error when the feature is disabled.
91    /// # Errors
92    ///
93    /// Returns an error when the requested operation cannot be completed.
94    pub fn compile(&self, engine_id: &str, core: &QueryCore) -> Result<CompiledQuery> {
95        let compiler = self.get(engine_id).ok_or_else(|| {
96            Error::Internal(format!(
97                "no query compiler registered for engine `{engine_id}` — enable the matching \
98                 valence-core `compiler-*` / valence public crate feature"
99            ))
100        })?;
101        compiler.compile(core)
102    }
103}
104
105static GLOBAL_REGISTRY: OnceLock<QueryCompilerRegistry> = OnceLock::new();
106
107/// Global registry populated from enabled compiler features.
108pub fn global_compiler_registry() -> &'static QueryCompilerRegistry {
109    GLOBAL_REGISTRY.get_or_init(QueryCompilerRegistry::with_enabled_features)
110}
111
112/// Compile `core` for the given backend engine id.
113/// # Errors
114///
115/// Returns an error when the requested operation cannot be completed.
116pub fn compile_for_engine(engine_id: &str, core: &QueryCore) -> Result<CompiledQuery> {
117    global_compiler_registry().compile(engine_id, core)
118}