Skip to main content

aion_store_libsql/
config.rs

1//! Deserialize-only configuration for the libSQL event store.
2
3use std::path::PathBuf;
4
5use serde::Deserialize;
6
7/// Operator-provided libSQL settings.
8///
9/// Durability, WAL, and replica sync values are optional because this crate does not assume
10/// defaults for operator tunables; absent values remain unset until connection code applies only
11/// the values explicitly provided here.
12#[derive(Debug, Deserialize)]
13pub struct LibSqlConfig {
14    /// Connection mode for the embedded libSQL store.
15    pub mode: LibSqlMode,
16    /// Optional libSQL journal mode, such as a WAL mode chosen by the operator.
17    pub journal_mode: Option<String>,
18    /// Optional libSQL synchronous setting chosen by the operator.
19    pub synchronous: Option<String>,
20    /// Optional replica sync interval in seconds, used only for embedded-replica mode.
21    pub sync_interval_seconds: Option<u64>,
22}
23
24/// Selects whether libSQL opens a local embedded file or an embedded replica.
25#[derive(Debug, Deserialize)]
26pub enum LibSqlMode {
27    /// Embedded local-file mode.
28    Embedded {
29        /// Path to the local libSQL database file.
30        path: PathBuf,
31    },
32    /// Embedded replica mode, using a local file synchronized with a remote primary.
33    EmbeddedReplica {
34        /// Path to the local replica database file.
35        path: PathBuf,
36        /// Remote primary URL used by libSQL replica sync.
37        primary_url: String,
38        /// Authentication token for the remote primary.
39        auth_token: String,
40    },
41}
42
43#[cfg(test)]
44mod tests {
45    use std::path::Path;
46
47    use super::{LibSqlConfig, LibSqlMode};
48
49    #[test]
50    fn deserializes_embedded_mode() -> Result<(), Box<dyn std::error::Error>> {
51        let config: LibSqlConfig = serde_json::from_str(
52            r#"{
53                "mode": {
54                    "Embedded": {
55                        "path": "app.db"
56                    }
57                },
58                "journal_mode": "wal",
59                "synchronous": "normal",
60                "sync_interval_seconds": 15
61            }"#,
62        )?;
63
64        match config.mode {
65            LibSqlMode::Embedded { path } => assert_eq!(path, Path::new("app.db")),
66            LibSqlMode::EmbeddedReplica { .. } => {
67                return Err("expected embedded mode".into());
68            }
69        }
70        assert_eq!(config.journal_mode.as_deref(), Some("wal"));
71        assert_eq!(config.synchronous.as_deref(), Some("normal"));
72        assert_eq!(config.sync_interval_seconds, Some(15));
73
74        Ok(())
75    }
76
77    #[test]
78    fn deserializes_embedded_replica_mode() -> Result<(), Box<dyn std::error::Error>> {
79        let config: LibSqlConfig = serde_json::from_str(
80            r#"{
81                "mode": {
82                    "EmbeddedReplica": {
83                        "path": "replica.db",
84                        "primary_url": "libsql://primary.example.com",
85                        "auth_token": "secret-token"
86                    }
87                }
88            }"#,
89        )?;
90
91        match config.mode {
92            LibSqlMode::EmbeddedReplica {
93                path,
94                primary_url,
95                auth_token,
96            } => {
97                assert_eq!(path, Path::new("replica.db"));
98                assert_eq!(primary_url, "libsql://primary.example.com");
99                assert_eq!(auth_token, "secret-token");
100            }
101            LibSqlMode::Embedded { .. } => {
102                return Err("expected embedded-replica mode".into());
103            }
104        }
105
106        Ok(())
107    }
108
109    #[test]
110    fn embedded_replica_requires_primary_url() {
111        let result = serde_json::from_str::<LibSqlConfig>(
112            r#"{
113                "mode": {
114                    "EmbeddedReplica": {
115                        "path": "replica.db",
116                        "auth_token": "secret-token"
117                    }
118                }
119            }"#,
120        );
121
122        assert!(result.is_err());
123    }
124
125    #[test]
126    fn omitted_tunables_remain_unset() -> Result<(), Box<dyn std::error::Error>> {
127        let config: LibSqlConfig = serde_json::from_str(
128            r#"{
129                "mode": {
130                    "Embedded": {
131                        "path": "app.db"
132                    }
133                }
134            }"#,
135        )?;
136
137        assert!(config.journal_mode.is_none());
138        assert!(config.synchronous.is_none());
139        assert!(config.sync_interval_seconds.is_none());
140
141        Ok(())
142    }
143}