appcore_sync_sqlite/
config.rs1use crate::{SqliteSyncError, SqliteSyncResult};
12use std::fmt;
13use std::path::{Path, PathBuf};
14
15const MIB: u64 = 1024 * 1024;
16
17#[derive(Clone, PartialEq, Eq)]
19pub struct SqliteSyncConfig {
20 pub(crate) path: PathBuf,
21 pub(crate) max_database_bytes: u64,
22 pub(crate) max_outbox_entries: usize,
23 pub(crate) max_checkpoints: usize,
24 pub(crate) max_outbox_record_bytes: usize,
25 pub(crate) max_read_records: usize,
26 pub(crate) max_read_bytes: usize,
27 pub(crate) max_tombstones: usize,
28 pub(crate) max_connections: usize,
29 pub(crate) busy_timeout_ms: u64,
30 pub(crate) backup_pages_per_step: i32,
31}
32
33impl SqliteSyncConfig {
34 pub fn new(path: impl Into<PathBuf>) -> Self {
36 Self {
37 path: path.into(),
38 max_database_bytes: 512 * MIB,
39 max_outbox_entries: 10_000,
40 max_checkpoints: 10_000,
41 max_outbox_record_bytes: 48 * MIB as usize,
42 max_read_records: 4_096,
43 max_read_bytes: 16 * MIB as usize,
44 max_tombstones: 100_000,
45 max_connections: 8,
46 busy_timeout_ms: 5_000,
47 backup_pages_per_step: 128,
48 }
49 }
50
51 pub fn with_max_database_bytes(mut self, value: u64) -> Self {
53 self.max_database_bytes = value;
54 self
55 }
56
57 pub fn with_max_outbox_entries(mut self, value: usize) -> Self {
59 self.max_outbox_entries = value;
60 self
61 }
62
63 pub fn with_max_checkpoints(mut self, value: usize) -> Self {
65 self.max_checkpoints = value;
66 self
67 }
68
69 pub fn with_max_outbox_record_bytes(mut self, value: usize) -> Self {
71 self.max_outbox_record_bytes = value;
72 self
73 }
74
75 pub fn with_max_read_records(mut self, value: usize) -> Self {
77 self.max_read_records = value;
78 self
79 }
80
81 pub fn with_max_read_bytes(mut self, value: usize) -> Self {
83 self.max_read_bytes = value;
84 self
85 }
86
87 pub fn with_max_tombstones(mut self, value: usize) -> Self {
89 self.max_tombstones = value;
90 self
91 }
92
93 pub fn with_max_connections(mut self, value: usize) -> Self {
95 self.max_connections = value;
96 self
97 }
98
99 pub fn with_busy_timeout_ms(mut self, value: u64) -> Self {
101 self.busy_timeout_ms = value;
102 self
103 }
104
105 pub fn with_backup_pages_per_step(mut self, value: i32) -> Self {
107 self.backup_pages_per_step = value;
108 self
109 }
110
111 pub fn path(&self) -> &Path {
113 &self.path
114 }
115
116 pub(crate) fn validate(&self) -> SqliteSyncResult<()> {
117 if self.path.as_os_str().is_empty() {
118 return Err(SqliteSyncError::InvalidConfiguration("empty path"));
119 }
120 if !(8 * MIB..=8 * 1024 * MIB).contains(&self.max_database_bytes) {
121 return Err(SqliteSyncError::InvalidConfiguration(
122 "database byte bound is outside 8 MiB..=8 GiB",
123 ));
124 }
125 if !(1..=100_000).contains(&self.max_outbox_entries) {
126 return Err(SqliteSyncError::InvalidConfiguration(
127 "outbox entry bound is outside 1..=100000",
128 ));
129 }
130 if !(1..=100_000).contains(&self.max_checkpoints) {
131 return Err(SqliteSyncError::InvalidConfiguration(
132 "checkpoint bound is outside 1..=100000",
133 ));
134 }
135 if !(MIB as usize..=48 * MIB as usize).contains(&self.max_outbox_record_bytes) {
136 return Err(SqliteSyncError::InvalidConfiguration(
137 "outbox record bound is outside 1 MiB..=48 MiB",
138 ));
139 }
140 if !(1..=10_000).contains(&self.max_read_records) {
141 return Err(SqliteSyncError::InvalidConfiguration(
142 "read record bound is outside 1..=10000",
143 ));
144 }
145 if !(MIB as usize..=64 * MIB as usize).contains(&self.max_read_bytes) {
146 return Err(SqliteSyncError::InvalidConfiguration(
147 "read byte bound is outside 1 MiB..=64 MiB",
148 ));
149 }
150 if !(1..=1_000_000).contains(&self.max_tombstones) {
151 return Err(SqliteSyncError::InvalidConfiguration(
152 "tombstone bound is outside 1..=1000000",
153 ));
154 }
155 if !(1..=32).contains(&self.max_connections) {
156 return Err(SqliteSyncError::InvalidConfiguration(
157 "connection bound is outside 1..=32",
158 ));
159 }
160 if !(1..=60_000).contains(&self.busy_timeout_ms) {
161 return Err(SqliteSyncError::InvalidConfiguration(
162 "busy timeout is outside 1..=60000 ms",
163 ));
164 }
165 if !(1..=4_096).contains(&self.backup_pages_per_step) {
166 return Err(SqliteSyncError::InvalidConfiguration(
167 "backup step is outside 1..=4096 pages",
168 ));
169 }
170 Ok(())
171 }
172}
173
174impl fmt::Debug for SqliteSyncConfig {
175 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
176 formatter
177 .debug_struct("SqliteSyncConfig")
178 .field("path_configured", &!self.path.as_os_str().is_empty())
179 .field("max_database_bytes", &self.max_database_bytes)
180 .field("max_outbox_entries", &self.max_outbox_entries)
181 .field("max_checkpoints", &self.max_checkpoints)
182 .field("max_outbox_record_bytes", &self.max_outbox_record_bytes)
183 .field("max_read_records", &self.max_read_records)
184 .field("max_read_bytes", &self.max_read_bytes)
185 .field("max_tombstones", &self.max_tombstones)
186 .field("max_connections", &self.max_connections)
187 .field("busy_timeout_ms", &self.busy_timeout_ms)
188 .field("backup_pages_per_step", &self.backup_pages_per_step)
189 .finish()
190 }
191}