use crate::{AppConfig, DbPool, db::PoolHandler, error::AppError, handlers::FullSchema};
use moka::future::Cache;
use papaya::HashMap;
use rig::providers::openai as rig_openai;
use std::{ops::Deref, sync::Arc, time::Duration};
use tracing::{error, info};
#[derive(Clone)]
pub struct AppState(Arc<AppStateInner>);
pub struct AppStateInner {
pub config: AppConfig,
pub pools: Arc<HashMap<String, DbPool>>,
pub schema_cache: Cache<String, Arc<Result<FullSchema, AppError>>>,
pub openai_client: rig_openai::Client,
}
impl std::fmt::Debug for AppStateInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AppStateInner")
.field("config", &self.config)
.field("db_pools_count", &self.pools.len()) .finish_non_exhaustive()
}
}
impl std::fmt::Debug for AppState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AppState").field(&self.0).finish()
}
}
impl Deref for AppState {
type Target = AppStateInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AppState {
pub async fn new(config: AppConfig) -> Result<Self, anyhow::Error> {
let pools = HashMap::new();
for db_config in &config.databases {
info!(
"Connecting to database '{}' (type: {})",
db_config.name, db_config.db_type
);
match DbPool::try_new(db_config).await {
Ok(pool) => {
pools.pin().insert(db_config.name.clone(), pool);
}
Err(e) => {
error!("Failed to connect to database '{}': {}", db_config.name, e);
}
}
}
info!("Database connections established.");
let schema_cache = Cache::builder()
.time_to_live(Duration::from_secs(10 * 60))
.max_capacity(1)
.build();
info!("Initializing OpenAI client from environment...");
let openai_client = rig_openai::Client::from_env();
info!("OpenAI client initialized.");
let inner = AppStateInner {
config,
pools: Arc::new(pools),
schema_cache,
openai_client, };
Ok(Self(Arc::new(inner)))
}
#[cfg(test)]
pub fn new_for_test(config: AppConfig) -> Self {
let pools = Arc::new(HashMap::new());
let schema_cache = Cache::builder().build();
let openai_client = rig_openai::Client::from_env();
let inner = AppStateInner {
config,
pools,
schema_cache,
openai_client,
};
Self(Arc::new(inner))
}
}