gemini_adk_rs/session/
sqlite.rs1use std::path::PathBuf;
12
13use async_trait::async_trait;
14
15use super::{Session, SessionError, SessionId, SessionService};
16use crate::events::Event;
17
18#[derive(Debug, Clone)]
20pub struct SqliteSessionConfig {
21 pub db_path: PathBuf,
23}
24
25impl SqliteSessionConfig {
26 pub fn in_memory() -> Self {
28 Self {
29 db_path: PathBuf::from(":memory:"),
30 }
31 }
32}
33
34#[cfg(feature = "database-sessions")]
39type Backend = super::DatabaseSessionService;
40#[cfg(not(feature = "database-sessions"))]
41type Backend = super::InMemorySessionService;
42
43pub struct SqliteSessionService {
50 config: SqliteSessionConfig,
51 inner: Backend,
52}
53
54impl SqliteSessionService {
55 pub fn new(config: SqliteSessionConfig) -> Self {
60 let inner = Self::build_backend(&config);
61 Self { config, inner }
62 }
63
64 #[cfg(feature = "database-sessions")]
65 fn build_backend(config: &SqliteSessionConfig) -> Backend {
66 let path = config.db_path.to_string_lossy();
67 let url = if path == ":memory:" {
70 "sqlite::memory:".to_string()
71 } else {
72 format!("sqlite://{path}")
73 };
74 super::DatabaseSessionService::new(url)
75 }
76
77 #[cfg(not(feature = "database-sessions"))]
78 fn build_backend(config: &SqliteSessionConfig) -> Backend {
79 static WARN_ONCE: std::sync::Once = std::sync::Once::new();
83 WARN_ONCE.call_once(|| {
84 let msg = format!(
85 "SqliteSessionService: the `database-sessions` feature is not enabled — ignoring db_path '{}' and using in-memory storage (sessions are lost on restart)",
86 config.db_path.to_string_lossy()
87 );
88 tracing::warn!(target: "gemini_adk_rs::session", "{msg}");
89 });
90 super::InMemorySessionService::new()
91 }
92
93 pub fn db_path(&self) -> &std::path::Path {
95 &self.config.db_path
96 }
97}
98
99#[async_trait]
100impl SessionService for SqliteSessionService {
101 async fn create_session(&self, app_name: &str, user_id: &str) -> Result<Session, SessionError> {
102 self.inner.create_session(app_name, user_id).await
103 }
104
105 async fn get_session(&self, id: &SessionId) -> Result<Option<Session>, SessionError> {
106 self.inner.get_session(id).await
107 }
108
109 async fn list_sessions(
110 &self,
111 app_name: &str,
112 user_id: &str,
113 ) -> Result<Vec<Session>, SessionError> {
114 self.inner.list_sessions(app_name, user_id).await
115 }
116
117 async fn delete_session(&self, id: &SessionId) -> Result<(), SessionError> {
118 self.inner.delete_session(id).await
119 }
120
121 async fn append_event(&self, id: &SessionId, event: Event) -> Result<(), SessionError> {
122 self.inner.append_event(id, event).await
123 }
124
125 async fn get_events(&self, id: &SessionId) -> Result<Vec<Event>, SessionError> {
126 self.inner.get_events(id).await
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[tokio::test]
135 async fn create_and_get() {
136 let svc = SqliteSessionService::new(SqliteSessionConfig::in_memory());
137 let session = svc.create_session("app", "user").await.unwrap();
138 let fetched = svc.get_session(&session.id).await.unwrap();
139 assert!(fetched.is_some());
140 }
141
142 #[test]
143 fn db_path() {
144 let svc = SqliteSessionService::new(SqliteSessionConfig {
145 db_path: PathBuf::from("/tmp/test.db"),
146 });
147 assert_eq!(svc.db_path(), std::path::Path::new("/tmp/test.db"));
148 }
149
150 #[test]
151 fn in_memory_config() {
152 let config = SqliteSessionConfig::in_memory();
153 assert_eq!(config.db_path, PathBuf::from(":memory:"));
154 }
155}