1#[cfg(any(feature = "core", feature = "replication", feature = "sync"))]
4use std::path::PathBuf;
5#[cfg(any(feature = "replication", feature = "sync"))]
6use std::time::Duration;
7
8use deadpool::{
9 Runtime,
10 managed::{CreatePoolError, PoolConfig},
11};
12use libsql::Builder;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::{Manager, Pool, PoolBuilder};
17
18#[derive(Clone, Debug)]
43#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
44pub struct Config {
45 #[cfg_attr(feature = "serde", serde(flatten))]
47 pub database: Database,
48 #[cfg_attr(feature = "serde", serde(default))]
50 pub pool: PoolConfig,
51}
52
53impl Config {
54 #[must_use]
56 pub fn new(database: Database) -> Self {
57 Self {
58 database,
59 pool: PoolConfig::default(),
60 }
61 }
62
63 pub async fn create_pool(
69 self,
70 runtime: Option<Runtime>,
71 ) -> Result<Pool, CreatePoolError<ConfigError>> {
72 let mut builder = self.builder().await.map_err(CreatePoolError::Config)?;
73 if let Some(runtime) = runtime {
74 builder = builder.runtime(runtime);
75 }
76 builder.build().map_err(CreatePoolError::Build)
77 }
78
79 pub async fn builder(self) -> Result<PoolBuilder, ConfigError> {
85 let config = self.pool;
86 let manager = Manager::from_config(self).await?;
87 Ok(Pool::builder(manager).config(config))
88 }
89}
90
91#[derive(Clone, Debug)]
92#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
93#[cfg_attr(feature = "serde", serde(tag = "database"))]
94pub enum Database {
97 #[cfg(feature = "core")]
99 Local(Local),
100 #[cfg(feature = "replication")]
102 LocalReplica(LocalReplica),
103 #[cfg(feature = "remote")]
105 Remote(Remote),
106 #[cfg(feature = "replication")]
108 RemoteReplica(RemoteReplica),
109 #[cfg(feature = "sync")]
111 SyncedDatabase(SyncedDatabase),
112}
113
114impl Database {
115 pub(crate) async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
116 match self {
117 #[cfg(feature = "core")]
118 Self::Local(x) => x.libsql_database().await,
119 #[cfg(feature = "replication")]
120 Self::LocalReplica(x) => x.libsql_database().await,
121 #[cfg(feature = "remote")]
122 Self::Remote(x) => x.libsql_database().await,
123 #[cfg(feature = "replication")]
124 Self::RemoteReplica(x) => x.libsql_database().await,
125 #[cfg(feature = "sync")]
126 Self::SyncedDatabase(x) => x.libsql_database().await,
127 #[cfg(not(any(
128 feature = "core",
129 feature = "replication",
130 feature = "remote",
131 feature = "sync"
132 )))]
133 _ => compile_error!(
134 "At least one of the following features must be enabled: core, replication, remote, sync"
135 ),
136 }
137 }
138}
139
140#[cfg(feature = "core")]
141#[derive(Clone, Debug)]
142#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
143#[allow(missing_docs)]
144pub struct Local {
145 pub path: PathBuf,
146 pub encryption_config: Option<EncryptionConfig>,
147 pub flags: Option<OpenFlags>,
148}
149
150#[cfg(feature = "core")]
151impl Local {
152 async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
153 let mut builder = Builder::new_local(&self.path);
154 if let Some(encryption_config) = &self.encryption_config {
155 builder = builder.encryption_config(encryption_config.to_libsql());
156 }
157 if let Some(flags) = &self.flags {
158 builder = builder.flags(flags.to_libsql());
159 }
160 builder.build().await
161 }
162}
163
164#[cfg(any(feature = "core", feature = "replication"))]
165#[derive(Clone, Debug)]
166#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
167#[allow(missing_docs)]
168pub struct EncryptionConfig {
169 pub cipher: Cipher,
170 pub encryption_key: bytes::Bytes,
171}
172
173#[cfg(feature = "core")]
174impl EncryptionConfig {
175 fn to_libsql(&self) -> libsql::EncryptionConfig {
176 libsql::EncryptionConfig {
177 cipher: self.cipher.to_libsql(),
178 encryption_key: self.encryption_key.clone(),
179 }
180 }
181}
182
183#[cfg(any(feature = "core", feature = "replication"))]
184#[derive(Clone, Copy, Debug, Default)]
185#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
186pub enum Cipher {
188 #[default]
189 #[cfg_attr(feature = "serde", serde(rename = "aes256cbc"))]
190 Aes256Cbc,
192}
193
194#[cfg(feature = "core")]
195impl Cipher {
196 fn to_libsql(self) -> libsql::Cipher {
197 match self {
198 Self::Aes256Cbc => libsql::Cipher::Aes256Cbc,
199 }
200 }
201}
202
203#[cfg(any(feature = "core", feature = "replication"))]
204#[derive(Copy, Clone, Debug)]
205#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
206#[allow(missing_docs)]
207pub struct OpenFlags {
208 pub read_only: bool,
209 pub read_write: bool,
210 pub create: bool,
211}
212
213#[cfg(any(feature = "core", feature = "replication"))]
214impl OpenFlags {
215 fn to_libsql(self) -> libsql::OpenFlags {
216 (if self.read_only {
217 libsql::OpenFlags::SQLITE_OPEN_READ_ONLY
218 } else {
219 libsql::OpenFlags::empty()
220 }) | (if self.read_write {
221 libsql::OpenFlags::SQLITE_OPEN_READ_WRITE
222 } else {
223 libsql::OpenFlags::empty()
224 }) | (if self.create {
225 libsql::OpenFlags::SQLITE_OPEN_CREATE
226 } else {
227 libsql::OpenFlags::empty()
228 })
229 }
230}
231
232#[cfg(feature = "replication")]
233#[derive(Clone, Debug)]
234#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
235#[allow(missing_docs)]
236pub struct LocalReplica {
237 pub path: PathBuf,
238 pub encryption_config: Option<EncryptionConfig>,
239 pub flags: Option<OpenFlags>,
240}
241
242#[cfg(feature = "replication")]
243impl LocalReplica {
244 async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
245 let mut builder = Builder::new_local_replica(&self.path);
246 if let Some(flags) = &self.flags {
247 builder = builder.flags(flags.to_libsql());
248 }
249 builder.build().await
251 }
252}
253
254#[cfg(feature = "remote")]
255#[derive(Clone, Debug)]
256#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
257#[allow(missing_docs)]
258pub struct Remote {
259 pub url: String,
260 pub auth_token: String,
261 pub namespace: Option<String>,
262 pub remote_encryption: Option<EncryptionContext>,
263}
264
265#[cfg(feature = "remote")]
266impl Remote {
267 async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
268 let mut builder = Builder::new_remote(self.url.clone(), self.auth_token.clone());
269 if let Some(namespace) = &self.namespace {
271 builder = builder.namespace(namespace);
272 }
273 #[allow(unused)]
274 if let Some(encryption_context) = &self.remote_encryption {
275 #[cfg(feature = "sync")]
276 {
277 builder = builder.remote_encryption(encryption_context.to_libsql());
278 }
279 #[cfg(not(feature = "sync"))]
280 return Err(libsql::Error::Misuse(
281 "Remote encryption unavailable: sync feature of libsql is disabled".into(),
282 ));
283 }
284 builder.build().await
285 }
286}
287
288#[cfg(feature = "replication")]
289#[derive(Clone, Debug)]
290#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
291#[allow(missing_docs)]
292pub struct RemoteReplica {
293 pub path: PathBuf,
294 pub url: String,
295 pub auth_token: String,
296 pub encryption_config: Option<EncryptionConfig>,
298 pub namespace: Option<String>,
300 pub read_your_writes: Option<bool>,
301 pub remote_encryption: Option<EncryptionContext>,
302 pub sync_interval: Option<Duration>,
303 pub sync_protocol: Option<SyncProtocol>,
304}
305
306#[cfg(feature = "replication")]
307impl RemoteReplica {
308 async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
309 let mut builder =
311 Builder::new_remote_replica(&self.path, self.url.clone(), self.auth_token.clone());
312 #[allow(unused)]
314 if let Some(encryption_config) = &self.encryption_config {
315 #[cfg(feature = "core")]
316 {
317 builder = builder.encryption_config(encryption_config.to_libsql());
318 }
319 #[cfg(not(feature = "core"))]
320 return Err(libsql::Error::Misuse("RemoteReplicate::encryption_config unavailable: core feature of libsql is disabled".into()));
321 }
322 if let Some(namespace) = &self.namespace {
324 builder = builder.namespace(namespace);
325 }
326 if let Some(read_your_writes) = self.read_your_writes {
327 builder = builder.read_your_writes(read_your_writes);
328 }
329 #[allow(unused)]
330 if let Some(encryption_context) = &self.remote_encryption {
331 #[cfg(feature = "sync")]
332 {
333 builder = builder.remote_encryption(encryption_context.to_libsql());
334 }
335 #[cfg(not(feature = "sync"))]
336 return Err(libsql::Error::Misuse("RemoteReplication::encryption_context unavailable: sync feature of libsql is disabled".into()));
337 }
338 if let Some(sync_interval) = &self.sync_interval {
339 builder = builder.sync_interval(*sync_interval);
340 }
341 #[allow(unused)]
342 if let Some(sync_protocol) = &self.sync_protocol {
343 #[cfg(feature = "sync")]
344 {
345 builder = builder.sync_protocol(sync_protocol.to_libsql());
346 }
347 #[cfg(not(feature = "sync"))]
348 return Err(libsql::Error::Misuse(
349 "RemoteReplication::sync_protocol unavailable: sync feature of libsql is disabled"
350 .into(),
351 ));
352 }
353 builder.build().await
354 }
355}
356
357#[cfg(any(feature = "remote", feature = "replication", feature = "sync"))]
358#[derive(Clone, Debug)]
359#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
360pub struct EncryptionContext {
362 pub key: EncryptionKey,
364}
365
366#[cfg(feature = "sync")]
367impl EncryptionContext {
368 #[cfg(feature = "sync")]
369 fn to_libsql(&self) -> libsql::EncryptionContext {
370 libsql::EncryptionContext {
371 key: self.key.to_libsql(),
372 }
373 }
374}
375
376#[cfg(any(feature = "remote", feature = "replication", feature = "sync"))]
377#[derive(Clone, Debug)]
378#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
379pub enum EncryptionKey {
381 Base64Encoded(String),
383 Bytes(Vec<u8>),
385}
386
387#[cfg(any(feature = "remote", feature = "sync"))]
388impl EncryptionKey {
389 #[cfg(feature = "sync")]
390 fn to_libsql(&self) -> libsql::EncryptionKey {
391 #[cfg(feature = "sync")]
392 match self {
393 Self::Base64Encoded(string) => libsql::EncryptionKey::Base64Encoded(string.clone()),
394 Self::Bytes(bytes) => libsql::EncryptionKey::Bytes(bytes.clone()),
395 }
396 }
397}
398
399#[cfg(feature = "replication")]
400#[derive(Clone, Copy, Debug)]
401#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
402pub enum SyncProtocol {
404 #[allow(missing_docs)]
405 V1,
406 #[allow(missing_docs)]
407 V2,
408}
409
410#[cfg(all(feature = "replication", feature = "sync"))]
411impl SyncProtocol {
412 fn to_libsql(self) -> libsql::SyncProtocol {
413 match self {
414 Self::V1 => libsql::SyncProtocol::V1,
415 Self::V2 => libsql::SyncProtocol::V2,
416 }
417 }
418}
419
420#[cfg(feature = "sync")]
421#[derive(Clone, Debug)]
422#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
423#[allow(missing_docs)]
424pub struct SyncedDatabase {
425 pub path: PathBuf,
426 pub url: String,
427 pub auth_token: String,
428 pub read_your_writes: Option<bool>,
430 pub remote_encryption: Option<EncryptionContext>,
431 pub remote_writes: Option<bool>,
432 pub set_push_batch_size: Option<u32>,
433 pub sync_interval: Option<Duration>,
434}
435
436#[cfg(feature = "sync")]
437impl SyncedDatabase {
438 async fn libsql_database(&self) -> Result<libsql::Database, libsql::Error> {
439 let mut builder =
440 Builder::new_synced_database(&self.path, self.url.clone(), self.auth_token.clone());
441 if let Some(read_your_writes) = self.read_your_writes {
443 builder = builder.read_your_writes(read_your_writes);
444 }
445 if let Some(encryption_context) = &self.remote_encryption {
446 builder = builder.remote_encryption(encryption_context.to_libsql());
447 }
448 if let Some(remote_writes) = &self.remote_writes {
449 builder = builder.remote_writes(*remote_writes);
450 }
451 if let Some(push_batch_size) = &self.set_push_batch_size {
452 builder = builder.set_push_batch_size(*push_batch_size);
453 }
454 if let Some(sync_interval) = &self.sync_interval {
455 builder = builder.sync_interval(*sync_interval);
456 }
457 builder.build().await
458 }
459}
460
461pub type ConfigError = libsql::Error;