chronon_backend_sqlite/lib.rs
1//! `SQLite` [`SchedulerStore`](chronon_core::store::SchedulerStore) for Chronon.
2//!
3//! **Audience:** backend engineers and test authors needing embedded, file-backed persistence.
4//!
5//! ## Stack position
6//!
7//! ```text
8//! chronon (facade, `sqlite` feature) → chronon-backend-sqlite → chronon-backend-sql-common → chronon-core
9//! ```
10//!
11//! Suitable for single-process deployments and CI. SQLite allows one writer at a time;
12//! use PostgreSQL or the Redis composite backend for multi-worker claim throughput.
13//!
14//! ## Entry points
15//!
16//! - [`SqliteSchedulerStore::new`] — open a database file
17//! - [`SqliteSchedulerStore::connect`] — connect via URL (including `:memory:`)
18//!
19//! ## Example
20//!
21//! ```rust,no_run
22//! use chronon_backend_sqlite::SqliteSchedulerStore;
23//!
24//! # async fn example() -> chronon_core::Result<()> {
25//! let store = SqliteSchedulerStore::connect("sqlite://:memory:").await?;
26//! # Ok(())
27//! # }
28//! ```
29
30use std::path::Path;
31
32use chronon_backend_sql_common::SqlSchedulerStore;
33use chronon_core::Result;
34use sqlx::SqlitePool;
35
36/// SQLite-backed scheduler store.
37pub struct SqliteSchedulerStore {
38 inner: SqlSchedulerStore,
39}
40
41impl SqliteSchedulerStore {
42 /// Open a `SQLite` database at `path` (creates the file if missing).
43 ///
44 /// # Examples
45 ///
46 /// ```rust,no_run
47 /// use chronon_backend_sqlite::SqliteSchedulerStore;
48 ///
49 /// # async fn example() -> chronon_core::Result<()> {
50 /// let store = SqliteSchedulerStore::new("/var/lib/chronon/chronon.db").await?;
51 /// # Ok(())
52 /// # }
53 /// ```
54 ///
55 /// # Errors
56 ///
57 /// Returns a storage error when the database cannot be opened or schema bootstrap fails.
58 pub async fn new(path: impl AsRef<Path>) -> Result<Self> {
59 let url = format!("sqlite://{}?mode=rwc", path.as_ref().display());
60 Self::connect(&url).await
61 }
62
63 /// Connect using a `SQLite` connection URL.
64 ///
65 /// # Examples
66 ///
67 /// ```rust,no_run
68 /// use chronon_backend_sqlite::SqliteSchedulerStore;
69 ///
70 /// # async fn example() -> chronon_core::Result<()> {
71 /// let store = SqliteSchedulerStore::connect("sqlite://:memory:").await?;
72 /// # Ok(())
73 /// # }
74 /// ```
75 ///
76 /// # Errors
77 ///
78 /// Returns a storage error when the pool cannot connect or schema bootstrap fails.
79 pub async fn connect(url: &str) -> Result<Self> {
80 let inner = SqlSchedulerStore::connect_sqlite(url).await?;
81 Ok(Self { inner })
82 }
83
84 /// Wrap an existing pool (schema bootstrap runs).
85 ///
86 /// # Examples
87 ///
88 /// ```rust,no_run
89 /// use chronon_backend_sqlite::SqliteSchedulerStore;
90 /// use sqlx::sqlite::SqlitePoolOptions;
91 ///
92 /// # async fn example() -> chronon_core::Result<()> {
93 /// let pool = SqlitePoolOptions::new()
94 /// .connect("sqlite://:memory:")
95 /// .await
96 /// .expect("pool");
97 /// let store = SqliteSchedulerStore::from_pool(pool).await?;
98 /// # Ok(())
99 /// # }
100 /// ```
101 ///
102 /// # Errors
103 ///
104 /// Returns a storage error when schema bootstrap fails.
105 pub async fn from_pool(pool: SqlitePool) -> Result<Self> {
106 let inner = SqlSchedulerStore::from_sqlite_pool(pool).await?;
107 Ok(Self { inner })
108 }
109
110 /// Underlying connection pool.
111 ///
112 /// # Panics
113 ///
114 /// Panics if the inner pool is not `SQLite` (internal invariant violation).
115 #[must_use]
116 pub fn pool(&self) -> &SqlitePool {
117 match self.inner.pool() {
118 chronon_backend_sql_common::SqlPool::Sqlite(pool) => pool,
119 chronon_backend_sql_common::SqlPool::Postgres(_) => {
120 panic!("sqlite backend has non-sqlite pool")
121 }
122 }
123 }
124}
125
126impl std::fmt::Debug for SqliteSchedulerStore {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 f.debug_struct("SqliteSchedulerStore")
129 .finish_non_exhaustive()
130 }
131}
132
133chronon_backend_sql_common::delegate_scheduler_store!(SqliteSchedulerStore, inner);