use std::path::{Path, PathBuf};
use crate::config::{
ConfigSource, EmbeddingConfig, FalkorConfig, QdrantConfig, resolve_embedding_config,
resolve_falkordb_config, resolve_qdrant_config,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CoreContext {
project_root: PathBuf,
project_id: String,
database_url: Option<String>,
falkordb: Option<FalkorConfig>,
qdrant: Option<QdrantConfig>,
embedding: Option<EmbeddingConfig>,
daemon_url: String,
}
impl CoreContext {
pub fn build(
project_root: PathBuf,
project_id: String,
database_url: Option<String>,
source: &mut impl ConfigSource,
) -> Self {
let falkordb = resolve_falkordb_config(source);
let qdrant = resolve_qdrant_config(source);
let embedding = resolve_embedding_config(source);
let daemon_url = crate::daemon_url::daemon_url();
Self {
project_root,
project_id,
database_url,
falkordb,
qdrant,
embedding,
daemon_url,
}
}
pub fn project_root(&self) -> &Path {
&self.project_root
}
pub fn project_id(&self) -> &str {
&self.project_id
}
pub fn database_url(&self) -> Option<&str> {
self.database_url.as_deref()
}
pub fn falkordb(&self) -> Option<&FalkorConfig> {
self.falkordb.as_ref()
}
pub fn qdrant(&self) -> Option<&QdrantConfig> {
self.qdrant.as_ref()
}
pub fn embedding(&self) -> Option<&EmbeddingConfig> {
self.embedding.as_ref()
}
pub fn daemon_url(&self) -> &str {
&self.daemon_url
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::{EnvOnlySource, TEST_ENV_LOCK};
use std::sync::MutexGuard;
struct EnvGuard {
_lock: MutexGuard<'static, ()>,
}
impl EnvGuard {
fn new() -> Self {
let guard = Self {
_lock: TEST_ENV_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner()),
};
guard.clear();
guard
}
fn clear(&self) {
for key in [
"GOBBY_FALKORDB_HOST",
"GOBBY_FALKORDB_PORT",
"GOBBY_FALKORDB_PASSWORD",
"GOBBY_QDRANT_URL",
"GOBBY_QDRANT_API_KEY",
"GOBBY_EMBEDDING_URL",
"GOBBY_EMBEDDING_MODEL",
"GOBBY_EMBEDDING_API_KEY",
] {
unsafe { std::env::remove_var(key) };
}
}
fn set(&self, key: &str, value: &str) {
unsafe { std::env::set_var(key, value) };
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
self.clear();
}
}
#[test]
fn missing_optional_services_are_none() {
let _env = EnvGuard::new();
let mut source = EnvOnlySource;
let root = std::path::PathBuf::from("/tmp/gobby-project");
let context = CoreContext::build(root.clone(), "project-id".to_string(), None, &mut source);
assert_eq!(context.project_root(), root.as_path());
assert_eq!(context.project_id(), "project-id");
assert_eq!(context.database_url(), None);
assert!(context.falkordb().is_none());
assert!(context.qdrant().is_none());
assert!(context.embedding().is_none());
assert!(!context.daemon_url().is_empty());
}
#[test]
fn build_with_env_only_source() {
let env = EnvGuard::new();
env.set("GOBBY_FALKORDB_HOST", "env-falkor.local");
env.set("GOBBY_FALKORDB_PORT", "17000");
env.set("GOBBY_QDRANT_URL", "http://env-qdrant:6333");
env.set("GOBBY_EMBEDDING_URL", "http://env-embedding:11434");
env.set("GOBBY_EMBEDDING_MODEL", "env-model");
let mut source = EnvOnlySource;
let root = std::path::PathBuf::from("/tmp/gobby-project");
let context = CoreContext::build(
root.clone(),
"project-id".to_string(),
Some("postgres://example".to_string()),
&mut source,
);
assert_eq!(context.project_root(), root.as_path());
assert_eq!(context.project_id(), "project-id");
assert_eq!(context.database_url(), Some("postgres://example"));
assert_eq!(
context.falkordb().map(|c| c.host.as_str()),
Some("env-falkor.local")
);
assert_eq!(
context.qdrant().and_then(|c| c.url.as_deref()),
Some("http://env-qdrant:6333")
);
assert_eq!(
context.embedding().map(|c| c.api_base.as_str()),
Some("http://env-embedding:11434")
);
assert_eq!(
context.embedding().map(|c| c.model.as_str()),
Some("env-model")
);
assert!(!context.daemon_url().is_empty());
}
}