Skip to main content

rs_adk/session/
sqlite.rs

1//! SQLite session service — lightweight persistent session storage.
2//!
3//! Mirrors ADK-Python's `sqlite_session_service`. Provides session
4//! persistence using a local SQLite database file.
5
6use std::path::PathBuf;
7
8use async_trait::async_trait;
9
10use super::{Session, SessionError, SessionId, SessionService};
11use crate::events::Event;
12
13/// Configuration for the SQLite session service.
14#[derive(Debug, Clone)]
15pub struct SqliteSessionConfig {
16    /// Path to the SQLite database file.
17    pub db_path: PathBuf,
18}
19
20impl SqliteSessionConfig {
21    /// Create a config for an in-memory SQLite database.
22    pub fn in_memory() -> Self {
23        Self {
24            db_path: PathBuf::from(":memory:"),
25        }
26    }
27}
28
29/// Session service backed by SQLite.
30///
31/// Provides lightweight, file-based session persistence suitable for
32/// single-process deployments and development environments.
33///
34/// The database schema is automatically created on first use.
35pub struct SqliteSessionService {
36    config: SqliteSessionConfig,
37    // In a real implementation, this would hold a connection pool.
38    // For now, we delegate to InMemorySessionService as a stub.
39    inner: super::InMemorySessionService,
40}
41
42impl SqliteSessionService {
43    /// Create a new SQLite session service.
44    ///
45    /// Initializes the database schema if it doesn't exist.
46    pub fn new(config: SqliteSessionConfig) -> Self {
47        // In a real implementation, this would:
48        // 1. Open/create the SQLite database file
49        // 2. Run the SQLITE_SCHEMA migration
50        // 3. Return a connected service
51        Self {
52            config,
53            inner: super::InMemorySessionService::new(),
54        }
55    }
56
57    /// Returns the configured database path.
58    pub fn db_path(&self) -> &std::path::Path {
59        &self.config.db_path
60    }
61}
62
63#[async_trait]
64impl SessionService for SqliteSessionService {
65    async fn create_session(&self, app_name: &str, user_id: &str) -> Result<Session, SessionError> {
66        // Stub: delegates to in-memory implementation.
67        // A real implementation would INSERT INTO sessions ...
68        self.inner.create_session(app_name, user_id).await
69    }
70
71    async fn get_session(&self, id: &SessionId) -> Result<Option<Session>, SessionError> {
72        self.inner.get_session(id).await
73    }
74
75    async fn list_sessions(
76        &self,
77        app_name: &str,
78        user_id: &str,
79    ) -> Result<Vec<Session>, SessionError> {
80        self.inner.list_sessions(app_name, user_id).await
81    }
82
83    async fn delete_session(&self, id: &SessionId) -> Result<(), SessionError> {
84        self.inner.delete_session(id).await
85    }
86
87    async fn append_event(&self, id: &SessionId, event: Event) -> Result<(), SessionError> {
88        self.inner.append_event(id, event).await
89    }
90
91    async fn get_events(&self, id: &SessionId) -> Result<Vec<Event>, SessionError> {
92        self.inner.get_events(id).await
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[tokio::test]
101    async fn create_and_get() {
102        let svc = SqliteSessionService::new(SqliteSessionConfig::in_memory());
103        let session = svc.create_session("app", "user").await.unwrap();
104        let fetched = svc.get_session(&session.id).await.unwrap();
105        assert!(fetched.is_some());
106    }
107
108    #[test]
109    fn db_path() {
110        let svc = SqliteSessionService::new(SqliteSessionConfig {
111            db_path: PathBuf::from("/tmp/test.db"),
112        });
113        assert_eq!(svc.db_path(), std::path::Path::new("/tmp/test.db"));
114    }
115
116    #[test]
117    fn in_memory_config() {
118        let config = SqliteSessionConfig::in_memory();
119        assert_eq!(config.db_path, PathBuf::from(":memory:"));
120    }
121}