Skip to content

Commit 3734c38

Browse files
committed
feat: Add types to show API shape and design
- Once we have consensus on the API and types, I'll move on to implementation details and tests
1 parent 726b975 commit 3734c38

5 files changed

Lines changed: 159 additions & 1 deletion

File tree

crates/sqlx-sqlite-conn-mgr/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ rust-version = "1.89"
88

99
[dependencies]
1010
sqlx = { version = "0.8.6", features = ["runtime-tokio", "sqlite"] }
11+
thiserror = "2.0.17"
1112
tokio = { version = "1.48.0", features = ["full"] }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! SQLite database with connection pooling and optional write access
2+
3+
use sqlx::{Pool, Sqlite};
4+
use std::path::PathBuf;
5+
use std::sync::atomic::AtomicBool;
6+
7+
/// SQLite database with connection pooling for concurrent reads and optional exclusive writes.
8+
///
9+
/// ## Architecture
10+
///
11+
/// The database maintains two connection pools:
12+
/// - **`read_pool`**: Pool of read-only connections for concurrent SELECT queries
13+
/// - **`write_conn`**: Single-connection pool for exclusive write access (enforced by max_connections=1)
14+
///
15+
/// ## State Management
16+
///
17+
/// - **`wal_initialized`**: Tracks whether WAL journal mode has been enabled (lazy initialization)
18+
/// - **`closed`**: Prevents use after the database has been closed
19+
/// - **`path`**: Database file path for cleanup operations
20+
///
21+
/// ## Usage Pattern
22+
///
23+
/// ```text
24+
/// 1. Connect to database (creates/reuses connection pools)
25+
/// 2. Read operations: Access read_pool for concurrent queries
26+
/// 3. Write operations: Acquire writer (lazily enables WAL on first call)
27+
/// 4. Close database when done
28+
/// ```
29+
#[derive(Debug)]
30+
pub struct SqliteDatabase {
31+
/// Pool of read-only connections (max 6) for concurrent SELECT queries
32+
read_pool: Pool<Sqlite>,
33+
34+
/// Single read-write connection pool (max 1) for serialized writes
35+
write_conn: Pool<Sqlite>,
36+
37+
/// Tracks if WAL mode has been initialized (set on first write)
38+
wal_initialized: AtomicBool,
39+
40+
/// Marks database as closed to prevent further operations
41+
closed: AtomicBool,
42+
43+
/// Path to database file (used for cleanup and registry lookups)
44+
path: PathBuf,
45+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! Error types for sqlx-sqlite-conn-mgr
2+
3+
use thiserror::Error;
4+
5+
/// Errors that may occur when working with sqlx-sqlite-conn-mgr
6+
#[derive(Error, Debug)]
7+
pub enum Error {
8+
/// IO error when accessing database files. Standard library IO errors
9+
/// are converted to this variant.
10+
#[error("IO error: {0}")]
11+
Io(#[from] std::io::Error),
12+
13+
/// Error from the sqlx library. Standard sqlx errors are converted to this variant
14+
#[error("Sqlx error: {0}")]
15+
Sqlx(#[from] sqlx::Error),
16+
17+
/// Database has been closed and cannot be used
18+
#[error("Database has been closed")]
19+
DatabaseClosed,
20+
}
21+
22+
/// A type alias for Results with our Error type
23+
pub type Result<T> = std::result::Result<T, Error>;
Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,26 @@
1-
//! # SQLx Connection Pool Manager
1+
//! # sqlx-sqlite-conn-mgr
2+
//!
3+
//! A minimal wrapper around SQLx that enforces pragmatic SQLite connection policies
4+
//! for mobile and desktop applications.
5+
//!
6+
//! ## Core Types
7+
//!
8+
//! - **[`SqliteDatabase`]**: Main database type with separate read and write connection pools
9+
//! - **[`WriteGuard`]**: RAII guard ensuring exclusive write access
10+
//! - **[`Error`]**: Error type for database operations
11+
//!
12+
//! ## Architecture
13+
//!
14+
//! - **Dual pools**: Separate read-only pool (max 6 connections) and write pool (max 1 connection)
15+
//! - **Lazy WAL mode**: Write-Ahead Logging enabled automatically on first write
16+
//! - **Exclusive writes**: Single-connection write pool enforces serialized write access
17+
//! - **Concurrent reads**: Multiple readers can query simultaneously via the read pool
18+
19+
mod database;
20+
mod error;
21+
mod write_guard;
22+
23+
// Re-export public types
24+
pub use database::SqliteDatabase;
25+
pub use error::{Error, Result};
26+
pub use write_guard::WriteGuard;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//! WriteGuard for exclusive write access to the database
2+
3+
use sqlx::Sqlite;
4+
use sqlx::pool::PoolConnection;
5+
use sqlx::sqlite::SqliteConnection;
6+
use std::ops::{Deref, DerefMut};
7+
8+
/// RAII guard for exclusive write access to a database connection
9+
///
10+
/// This guard wraps a pool connection and returns it to the pool on drop.
11+
/// Only one `WriteGuard` can exist at a time (enforced by max_connections=1),
12+
/// ensuring serialized write access.
13+
///
14+
/// The guard derefs to `SqliteConnection` allowing direct use with sqlx queries.
15+
///
16+
/// # Example
17+
///
18+
/// ```no_run
19+
/// use sqlx_sqlite_conn_mgr::SqliteDatabase;
20+
/// use sqlx::query;
21+
///
22+
/// # async fn example() -> Result<(), sqlx_sqlite_conn_mgr::Error> {
23+
/// let db = SqliteDatabase::connect("test.db").await?;
24+
/// let mut writer = db.acquire_writer().await?;
25+
/// // Use &mut *writer for INSERT/UPDATE/DELETE queries
26+
/// query("INSERT INTO users (name) VALUES (?)")
27+
/// .bind("Alice")
28+
/// .execute(&mut *writer)
29+
/// .await?;
30+
/// // Writer is automatically returned when dropped
31+
/// # Ok(())
32+
/// # }
33+
/// ```
34+
#[derive(Debug)]
35+
pub struct WriteGuard {
36+
conn: PoolConnection<Sqlite>,
37+
}
38+
39+
impl WriteGuard {
40+
/// Create a new WriteGuard by taking ownership of a pool connection
41+
pub(crate) fn new(conn: PoolConnection<Sqlite>) -> Self {
42+
Self { conn }
43+
}
44+
}
45+
46+
impl Deref for WriteGuard {
47+
type Target = SqliteConnection;
48+
49+
fn deref(&self) -> &Self::Target {
50+
&self.conn
51+
}
52+
}
53+
54+
impl DerefMut for WriteGuard {
55+
fn deref_mut(&mut self) -> &mut Self::Target {
56+
&mut self.conn
57+
}
58+
}
59+
60+
// Drop is automatically implemented - PoolConnection returns itself to the pool
61+
62+
// WriteGuard must be Send to cross .await boundaries
63+
// This is safe because SqliteConnection is Send
64+
unsafe impl Send for WriteGuard {}

0 commit comments

Comments
 (0)