1use std::path::{Path, PathBuf};
25use std::time::Duration;
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum Source {
31 Memory,
33 File(PathBuf),
35 #[cfg(feature = "sync")]
38 #[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
39 Sync(SyncOptions),
40 #[cfg(feature = "serverless")]
42 #[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
43 Remote(RemoteOptions),
44}
45
46#[cfg(feature = "serverless")]
48#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
49#[derive(Clone, Debug, PartialEq, Eq)]
50#[non_exhaustive]
51pub struct RemoteOptions {
52 pub url: String,
54 pub auth_token: Option<String>,
56 pub remote_encryption_key: Option<String>,
58}
59
60#[cfg(feature = "sync")]
62#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
63#[derive(Clone, Debug, PartialEq, Eq)]
64pub struct SyncOptions {
65 pub path: PathBuf,
67 pub remote_url: String,
69 pub auth_token: Option<String>,
71 pub bootstrap_if_empty: bool,
74}
75
76#[derive(Clone, Debug, PartialEq, Eq)]
78pub struct Encryption {
79 pub cipher: String,
81 pub hexkey: String,
83}
84
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
88#[non_exhaustive]
89#[allow(
90 clippy::struct_excessive_bools,
91 reason = "the flags mirror the independent flags of turso::Builder"
92)]
93pub struct Experimental {
94 pub attach: bool,
96 pub custom_types: bool,
98 pub generated_columns: bool,
100 pub index_method: bool,
102 pub materialized_views: bool,
104 pub vacuum: bool,
106 pub multiprocess_wal: bool,
108 pub without_rowid: bool,
110}
111
112#[derive(Clone, Debug, PartialEq, Eq)]
125pub struct ConnectOptions {
126 pub(crate) source: Source,
128 pub(crate) read_only: bool,
130 pub(crate) encryption: Option<Encryption>,
132 pub(crate) experimental: Experimental,
134 pub(crate) max_connections: usize,
136 pub(crate) acquire_timeout: Duration,
138 pub(crate) busy_timeout: Option<Duration>,
140 pub(crate) foreign_keys: bool,
142 pub(crate) mvcc: bool,
144 pub(crate) pragmas: Vec<(String, String)>,
146}
147
148impl ConnectOptions {
149 fn with_source(source: Source) -> Self {
151 Self {
152 source,
153 read_only: false,
154 encryption: None,
155 experimental: Experimental::default(),
156 max_connections: 8,
157 acquire_timeout: Duration::from_secs(30),
158 busy_timeout: Some(Duration::from_secs(5)),
159 foreign_keys: true,
160 mvcc: false,
161 pragmas: Vec::new(),
162 }
163 }
164
165 pub fn new(path: impl AsRef<Path>) -> Self {
168 let path = path.as_ref();
169 if path.as_os_str() == ":memory:" {
170 Self::in_memory()
171 } else {
172 Self::with_source(Source::File(path.to_path_buf()))
173 }
174 }
175
176 pub fn in_memory() -> Self {
178 Self::with_source(Source::Memory)
179 }
180
181 #[cfg(feature = "sync")]
183 #[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
184 pub fn sync(path: impl AsRef<Path>, remote_url: impl Into<String>) -> Self {
185 Self::with_source(Source::Sync(SyncOptions {
186 path: path.as_ref().to_path_buf(),
187 remote_url: remote_url.into(),
188 auth_token: None,
189 bootstrap_if_empty: true,
190 }))
191 }
192
193 #[cfg(feature = "serverless")]
199 #[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
200 pub fn remote(url: impl Into<String>) -> Self {
201 Self::with_source(Source::Remote(RemoteOptions {
202 url: url.into(),
203 auth_token: None,
204 remote_encryption_key: None,
205 }))
206 }
207
208 #[cfg(any(feature = "sync", feature = "serverless"))]
211 #[cfg_attr(docsrs, doc(cfg(any(feature = "sync", feature = "serverless"))))]
212 #[must_use]
213 pub fn auth_token(mut self, token: impl Into<String>) -> Self {
214 match &mut self.source {
215 #[cfg(feature = "sync")]
216 Source::Sync(opts) => opts.auth_token = Some(token.into()),
217 #[cfg(feature = "serverless")]
218 Source::Remote(opts) => opts.auth_token = Some(token.into()),
219 _ => {}
220 }
221 self
222 }
223
224 #[cfg(feature = "serverless")]
227 #[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
228 #[must_use]
229 pub fn remote_encryption_key(mut self, key: impl Into<String>) -> Self {
230 if let Source::Remote(opts) = &mut self.source {
231 opts.remote_encryption_key = Some(key.into());
232 }
233 self
234 }
235
236 #[must_use]
238 pub fn read_only(mut self, read_only: bool) -> Self {
239 self.read_only = read_only;
240 self
241 }
242
243 #[must_use]
245 pub fn encryption(mut self, cipher: impl Into<String>, hexkey: impl Into<String>) -> Self {
246 self.encryption = Some(Encryption {
247 cipher: cipher.into(),
248 hexkey: hexkey.into(),
249 });
250 self
251 }
252
253 #[must_use]
255 pub fn experimental(mut self, experimental: Experimental) -> Self {
256 self.experimental = experimental;
257 self
258 }
259
260 #[must_use]
262 pub fn max_connections(mut self, max: usize) -> Self {
263 self.max_connections = max.max(1);
264 self
265 }
266
267 #[must_use]
269 pub fn acquire_timeout(mut self, timeout: Duration) -> Self {
270 self.acquire_timeout = timeout;
271 self
272 }
273
274 #[must_use]
277 pub fn busy_timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
278 self.busy_timeout = timeout.into();
279 self
280 }
281
282 #[must_use]
284 pub fn foreign_keys(mut self, enabled: bool) -> Self {
285 self.foreign_keys = enabled;
286 self
287 }
288
289 #[must_use]
294 pub fn mvcc(mut self, enabled: bool) -> Self {
295 self.mvcc = enabled;
296 self
297 }
298
299 #[must_use]
301 pub fn pragma(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
302 self.pragmas.push((name.into(), value.into()));
303 self
304 }
305
306 pub fn source(&self) -> &Source {
308 &self.source
309 }
310
311 pub fn max_connections_value(&self) -> usize {
313 self.max_connections
314 }
315
316 pub fn busy_timeout_value(&self) -> Option<Duration> {
318 self.busy_timeout
319 }
320
321 pub fn is_in_memory(&self) -> bool {
323 matches!(self.source, Source::Memory)
324 }
325
326 pub(crate) fn local_builder(&self, path: &str) -> turso::Builder {
329 let mut builder = turso::Builder::new_local(path).read_only(self.read_only);
330 if let Some(enc) = &self.encryption {
331 builder =
332 builder
333 .experimental_encryption(true)
334 .with_encryption(turso::EncryptionOpts {
335 cipher: enc.cipher.clone(),
336 hexkey: enc.hexkey.clone(),
337 });
338 }
339 let x = self.experimental;
340 builder
341 .experimental_attach(x.attach)
342 .experimental_custom_types(x.custom_types)
343 .experimental_generated_columns(x.generated_columns)
344 .experimental_index_method(x.index_method)
345 .experimental_materialized_views(x.materialized_views)
346 .experimental_vacuum(x.vacuum)
347 .experimental_multiprocess_wal(x.multiprocess_wal)
348 .experimental_without_rowid(x.without_rowid)
349 }
350}
351
352impl From<&str> for ConnectOptions {
353 fn from(path: &str) -> Self {
354 Self::new(path)
355 }
356}
357
358impl From<String> for ConnectOptions {
359 fn from(path: String) -> Self {
360 Self::new(path)
361 }
362}
363
364impl From<PathBuf> for ConnectOptions {
365 fn from(path: PathBuf) -> Self {
366 Self::new(path)
367 }
368}
369
370impl From<&Path> for ConnectOptions {
371 fn from(path: &Path) -> Self {
372 Self::new(path)
373 }
374}