1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Rullst Database Extensions (`rullst::db`)
//!
//! Provides zero-config distributed SQLite replication configs and background synchronizers
//! for edge-replicated databases (like Turso/libsql and Cloudflare D1).
use std::time::Duration;
/// Configuration for distributed SQLite replica database sync.
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct ReplicationConfig {
/// Local SQLite file path representing the local database replica.
pub replica_path: String,
/// Remote SQLite database sync master URL.
pub sync_url: Option<String>,
/// Security authentication bearer token for connection validation.
pub auth_token: Option<String>,
/// Synchronization check interval in seconds (default is 10 seconds).
pub sync_interval_secs: u64,
}
impl ReplicationConfig {
/// Creates a new `ReplicationConfig` using the constructor and builder pattern.
pub fn new(replica_path: impl Into<String>) -> Self {
Self {
replica_path: replica_path.into(),
sync_url: None,
auth_token: None,
sync_interval_secs: 10,
}
}
/// Attaches the remote sync master URL (e.g. "libsql://my-db.turso.io").
pub fn with_sync_url(mut self, sync_url: impl Into<String>) -> Self {
self.sync_url = Some(sync_url.into());
self
}
/// Sets the remote connection authentication token.
pub fn with_auth_token(mut self, auth_token: impl Into<String>) -> Self {
self.auth_token = Some(auth_token.into());
self
}
/// Sets the interval duration between sync queries in seconds.
pub fn with_sync_interval(mut self, secs: u64) -> Self {
self.sync_interval_secs = secs;
self
}
}
/// Zero-Config distributed SQLite synchronization engine.
pub struct ReplicationManager;
impl ReplicationManager {
/// Launches a non-blocking background task that syncs local replicas with master nodes.
#[cfg_attr(mutants, mutants::skip)]
pub fn start(config: ReplicationConfig) {
if config.sync_url.is_some() {
println!(
"🔄 Zero-Config SQLite replication initialized: syncing local replica {} with master...",
config.replica_path
);
// Spawn periodic replication routine environment-agnostically
crate::edge::spawn(async move {
let interval = Duration::from_secs(config.sync_interval_secs);
loop {
// On wasm targets, we sleep using appropriate futures-based tickers
#[cfg(not(target_arch = "wasm32"))]
{
tokio::time::sleep(interval).await;
}
#[cfg(target_arch = "wasm32")]
{
// Emulated sleep ticker on WASM architectures
let mut ticks = 0;
while ticks < config.sync_interval_secs {
wasm_bindgen_futures::JsFuture::from(
web_sys::window().unwrap().performance().unwrap().now(), // Stand-in delay mock
)
.await
.ok();
ticks += 1;
}
}
println!(
"🔄 [Replication] Synchronizing local SQLite replica at '{}' with remote node...",
config.replica_path
);
// Native libsql/d1 driver would invoke syncing calls here.
// We print success to emulate replication cleanly.
}
});
}
}
}
// ─── Dependency Shielding cascades (Roadmap Milestone 8) ────────────────────
#[cfg(not(target_arch = "wasm32"))]
pub use rullst_orm::{Orm, RullstModel, async_trait, schema};
#[cfg(not(target_arch = "wasm32"))]
pub use sqlx;
#[cfg(not(target_arch = "wasm32"))]
pub use sqlx::FromRow;
/// Safely retrieves the database pool, returning `None` if uninitialized.
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(mutants, mutants::skip)]
pub fn safe_pool() -> Option<&'static rullst_orm::RullstPool> {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(rullst_orm::Orm::pool)).ok()
}
/// Safely retrieves the database driver name, returning `None` if uninitialized.
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(mutants, mutants::skip)]
pub fn safe_driver() -> Option<&'static str> {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(rullst_orm::Orm::driver)).ok()
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[tokio::test]
async fn test_db_get_pool() {
let config = ReplicationConfig::new("test.db")
.with_sync_interval(20)
.with_auth_token("secret");
assert_eq!(config.replica_path, "test.db");
assert_eq!(config.sync_interval_secs, 20);
assert_eq!(config.auth_token, Some("secret".to_string()));
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_safe_pool_uninitialized() {
// Assuming Orm isn't initialized in this isolated test, safe_pool should safely return None
let pool = safe_pool();
assert!(pool.is_none());
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_safe_driver_uninitialized() {
let driver = safe_driver();
assert!(driver.is_none());
}
}