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