1use crate::{AppConfig, DbPool, db::PoolHandler, error::AppError, handlers::FullSchema};
2use moka::future::Cache;
3use papaya::HashMap;
4use rig::providers::openai as rig_openai;
5use std::{ops::Deref, sync::Arc, time::Duration};
6use tracing::{error, info}; #[derive(Clone)]
9pub struct AppState(Arc<AppStateInner>);
10
11pub struct AppStateInner {
12 pub config: AppConfig,
13 pub pools: Arc<HashMap<String, DbPool>>,
14 pub schema_cache: Cache<String, Arc<Result<FullSchema, AppError>>>,
16 pub openai_client: rig_openai::Client,
18}
19
20impl std::fmt::Debug for AppStateInner {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 f.debug_struct("AppStateInner")
24 .field("config", &self.config)
25 .field("db_pools_count", &self.pools.len()) .finish_non_exhaustive()
29 }
30}
31
32impl std::fmt::Debug for AppState {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 f.debug_tuple("AppState").field(&self.0).finish()
36 }
37}
38
39impl Deref for AppState {
40 type Target = AppStateInner;
41
42 fn deref(&self) -> &Self::Target {
43 &self.0
44 }
45}
46
47impl AppState {
48 pub async fn new(config: AppConfig) -> Result<Self, anyhow::Error> {
49 let pools = HashMap::new();
50
51 for db_config in &config.databases {
52 info!(
53 "Connecting to database '{}' (type: {})",
54 db_config.name, db_config.db_type
55 );
56 match DbPool::try_new(db_config).await {
57 Ok(pool) => {
58 pools.pin().insert(db_config.name.clone(), pool);
59 }
60 Err(e) => {
61 error!("Failed to connect to database '{}': {}", db_config.name, e);
62 }
63 }
64 }
65 info!("Database connections established.");
66
67 let schema_cache = Cache::builder()
69 .time_to_live(Duration::from_secs(10 * 60))
71 .max_capacity(1)
73 .build();
74
75 info!("Initializing OpenAI client from environment...");
79 let openai_client = rig_openai::Client::from_env();
80 info!("OpenAI client initialized.");
81
82 let inner = AppStateInner {
83 config,
84 pools: Arc::new(pools),
85 schema_cache,
86 openai_client, };
88 Ok(Self(Arc::new(inner)))
89 }
90
91 #[cfg(test)]
92 pub fn new_for_test(config: AppConfig) -> Self {
93 let pools = Arc::new(HashMap::new());
95 let schema_cache = Cache::builder().build();
96 let openai_client = rig_openai::Client::from_env();
100
101 let inner = AppStateInner {
102 config,
103 pools,
104 schema_cache,
105 openai_client,
106 };
107 Self(Arc::new(inner))
108 }
109}