use std::path::{Path, PathBuf};
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Source {
Memory,
File(PathBuf),
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
Sync(SyncOptions),
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
Remote(RemoteOptions),
}
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct RemoteOptions {
pub url: String,
pub auth_token: Option<String>,
pub remote_encryption_key: Option<String>,
}
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SyncOptions {
pub path: PathBuf,
pub remote_url: String,
pub auth_token: Option<String>,
pub bootstrap_if_empty: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Encryption {
pub cipher: String,
pub hexkey: String,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
#[allow(
clippy::struct_excessive_bools,
reason = "the flags mirror the independent flags of turso::Builder"
)]
pub struct Experimental {
pub attach: bool,
pub custom_types: bool,
pub generated_columns: bool,
pub index_method: bool,
pub materialized_views: bool,
pub vacuum: bool,
pub multiprocess_wal: bool,
pub without_rowid: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ConnectOptions {
pub(crate) source: Source,
pub(crate) read_only: bool,
pub(crate) encryption: Option<Encryption>,
pub(crate) experimental: Experimental,
pub(crate) max_connections: usize,
pub(crate) acquire_timeout: Duration,
pub(crate) busy_timeout: Option<Duration>,
pub(crate) foreign_keys: bool,
pub(crate) mvcc: bool,
pub(crate) pragmas: Vec<(String, String)>,
}
impl ConnectOptions {
fn with_source(source: Source) -> Self {
Self {
source,
read_only: false,
encryption: None,
experimental: Experimental::default(),
max_connections: 8,
acquire_timeout: Duration::from_secs(30),
busy_timeout: Some(Duration::from_secs(5)),
foreign_keys: true,
mvcc: false,
pragmas: Vec::new(),
}
}
pub fn new(path: impl AsRef<Path>) -> Self {
let path = path.as_ref();
if path.as_os_str() == ":memory:" {
Self::in_memory()
} else {
Self::with_source(Source::File(path.to_path_buf()))
}
}
pub fn in_memory() -> Self {
Self::with_source(Source::Memory)
}
#[cfg(feature = "sync")]
#[cfg_attr(docsrs, doc(cfg(feature = "sync")))]
pub fn sync(path: impl AsRef<Path>, remote_url: impl Into<String>) -> Self {
Self::with_source(Source::Sync(SyncOptions {
path: path.as_ref().to_path_buf(),
remote_url: remote_url.into(),
auth_token: None,
bootstrap_if_empty: true,
}))
}
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
pub fn remote(url: impl Into<String>) -> Self {
Self::with_source(Source::Remote(RemoteOptions {
url: url.into(),
auth_token: None,
remote_encryption_key: None,
}))
}
#[cfg(any(feature = "sync", feature = "serverless"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "sync", feature = "serverless"))))]
#[must_use]
pub fn auth_token(mut self, token: impl Into<String>) -> Self {
match &mut self.source {
#[cfg(feature = "sync")]
Source::Sync(opts) => opts.auth_token = Some(token.into()),
#[cfg(feature = "serverless")]
Source::Remote(opts) => opts.auth_token = Some(token.into()),
_ => {}
}
self
}
#[cfg(feature = "serverless")]
#[cfg_attr(docsrs, doc(cfg(feature = "serverless")))]
#[must_use]
pub fn remote_encryption_key(mut self, key: impl Into<String>) -> Self {
if let Source::Remote(opts) = &mut self.source {
opts.remote_encryption_key = Some(key.into());
}
self
}
#[must_use]
pub fn read_only(mut self, read_only: bool) -> Self {
self.read_only = read_only;
self
}
#[must_use]
pub fn encryption(mut self, cipher: impl Into<String>, hexkey: impl Into<String>) -> Self {
self.encryption = Some(Encryption {
cipher: cipher.into(),
hexkey: hexkey.into(),
});
self
}
#[must_use]
pub fn experimental(mut self, experimental: Experimental) -> Self {
self.experimental = experimental;
self
}
#[must_use]
pub fn max_connections(mut self, max: usize) -> Self {
self.max_connections = max.max(1);
self
}
#[must_use]
pub fn acquire_timeout(mut self, timeout: Duration) -> Self {
self.acquire_timeout = timeout;
self
}
#[must_use]
pub fn busy_timeout(mut self, timeout: impl Into<Option<Duration>>) -> Self {
self.busy_timeout = timeout.into();
self
}
#[must_use]
pub fn foreign_keys(mut self, enabled: bool) -> Self {
self.foreign_keys = enabled;
self
}
#[must_use]
pub fn mvcc(mut self, enabled: bool) -> Self {
self.mvcc = enabled;
self
}
#[must_use]
pub fn pragma(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.pragmas.push((name.into(), value.into()));
self
}
pub fn source(&self) -> &Source {
&self.source
}
pub fn max_connections_value(&self) -> usize {
self.max_connections
}
pub fn busy_timeout_value(&self) -> Option<Duration> {
self.busy_timeout
}
pub fn is_in_memory(&self) -> bool {
matches!(self.source, Source::Memory)
}
pub(crate) fn local_builder(&self, path: &str) -> turso::Builder {
let mut builder = turso::Builder::new_local(path).read_only(self.read_only);
if let Some(enc) = &self.encryption {
builder =
builder
.experimental_encryption(true)
.with_encryption(turso::EncryptionOpts {
cipher: enc.cipher.clone(),
hexkey: enc.hexkey.clone(),
});
}
let x = self.experimental;
builder
.experimental_attach(x.attach)
.experimental_custom_types(x.custom_types)
.experimental_generated_columns(x.generated_columns)
.experimental_index_method(x.index_method)
.experimental_materialized_views(x.materialized_views)
.experimental_vacuum(x.vacuum)
.experimental_multiprocess_wal(x.multiprocess_wal)
.experimental_without_rowid(x.without_rowid)
}
}
impl From<&str> for ConnectOptions {
fn from(path: &str) -> Self {
Self::new(path)
}
}
impl From<String> for ConnectOptions {
fn from(path: String) -> Self {
Self::new(path)
}
}
impl From<PathBuf> for ConnectOptions {
fn from(path: PathBuf) -> Self {
Self::new(path)
}
}
impl From<&Path> for ConnectOptions {
fn from(path: &Path) -> Self {
Self::new(path)
}
}