use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use crate::error::{Error, Result};
use crate::known_engines::KnownEngines;
use crate::query::QueryCore;
use crate::query_compiler::QueryCompiler;
use crate::CompiledQuery;
#[derive(Clone, Default)]
pub struct QueryCompilerRegistry {
compilers: HashMap<&'static str, Arc<dyn QueryCompiler>>,
}
impl std::fmt::Debug for QueryCompilerRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QueryCompilerRegistry")
.field("engines", &self.compilers.keys().collect::<Vec<_>>())
.finish()
}
}
impl QueryCompilerRegistry {
#[must_use]
pub fn with_enabled_features() -> Self {
let mut registry = Self::default();
registry.register_builtins();
registry
}
fn register_builtins(&mut self) {
#[cfg(feature = "compiler-sql")]
{
let sql: Arc<dyn QueryCompiler> = Arc::new(crate::backend::SqlQueryCompiler);
self.register(KnownEngines::SQLITE, Arc::clone(&sql));
self.register(KnownEngines::POSTGRES, Arc::clone(&sql));
self.register(KnownEngines::INMEMORY_MEM, sql);
}
#[cfg(feature = "compiler-surreal")]
{
self.register(
KnownEngines::SURREALDB,
Arc::new(crate::backend::SurrealQueryCompiler) as Arc<dyn QueryCompiler>,
);
}
#[cfg(feature = "compiler-mongodb")]
{
self.register(
KnownEngines::MONGODB,
Arc::new(crate::backend::MongoQueryCompiler) as Arc<dyn QueryCompiler>,
);
}
#[cfg(feature = "compiler-redis")]
{
self.register(
KnownEngines::REDIS,
Arc::new(crate::backend::RedisQueryCompiler) as Arc<dyn QueryCompiler>,
);
}
#[cfg(feature = "compiler-indradb")]
{
self.register(
KnownEngines::INDRADB,
Arc::new(crate::backend::IndraQueryCompiler) as Arc<dyn QueryCompiler>,
);
}
#[cfg(feature = "compiler-hybrid")]
{
self.register(
KnownEngines::HYBRID_INDRA_SQL,
Arc::new(crate::backend::HybridQueryCompiler) as Arc<dyn QueryCompiler>,
);
}
}
pub fn register(&mut self, engine_id: &'static str, compiler: Arc<dyn QueryCompiler>) {
self.compilers.insert(engine_id, compiler);
}
pub fn get(&self, engine_id: &str) -> Option<&Arc<dyn QueryCompiler>> {
self.compilers.get(engine_id)
}
pub fn compile(&self, engine_id: &str, core: &QueryCore) -> Result<CompiledQuery> {
let compiler = self.get(engine_id).ok_or_else(|| {
Error::Internal(format!(
"no query compiler registered for engine `{engine_id}` — enable the matching \
valence-core `compiler-*` / valence public crate feature"
))
})?;
compiler.compile(core)
}
}
static GLOBAL_REGISTRY: OnceLock<QueryCompilerRegistry> = OnceLock::new();
pub fn global_compiler_registry() -> &'static QueryCompilerRegistry {
GLOBAL_REGISTRY.get_or_init(QueryCompilerRegistry::with_enabled_features)
}
pub fn compile_for_engine(engine_id: &str, core: &QueryCore) -> Result<CompiledQuery> {
global_compiler_registry().compile(engine_id, core)
}