aboutsummaryrefslogtreecommitdiffstats
path: root/crates/turtle/src/atuin_client/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/turtle/src/atuin_client/utils.rs')
-rw-r--r--crates/turtle/src/atuin_client/utils.rs79
1 files changed, 78 insertions, 1 deletions
diff --git a/crates/turtle/src/atuin_client/utils.rs b/crates/turtle/src/atuin_client/utils.rs
index 6d178b77..989f9fc1 100644
--- a/crates/turtle/src/atuin_client/utils.rs
+++ b/crates/turtle/src/atuin_client/utils.rs
@@ -1,4 +1,3 @@
-
pub(crate) fn get_hostname() -> String {
std::env::var("ATUIN_HOST_NAME")
.unwrap_or_else(|_| whoami::hostname().unwrap_or_else(|_| "unknown-host".to_string()))
@@ -13,3 +12,81 @@ pub(crate) fn get_username() -> String {
pub(crate) fn get_host_user() -> String {
format!("{}:{}", get_hostname(), get_username())
}
+
+/// Setup a [`SQLite`] database.
+///
+/// This takes care of correct locking, so that we avoid a race when setting up the database.
+macro_rules! setup_db {
+ (
+ $db_path:expr,
+ $a_timeout:expr,
+ $opts:expr,
+ $m_name:literal $(,)?
+ ) => {{
+ async fn migrate(pool: &SqlitePool) -> sqlx::Result<()> {
+ { sqlx::sqlx_macros::migrate!($m_name) }.run(pool).await?;
+ Ok(())
+ }
+
+ crate::atuin_client::utils::setup_db_inner($db_path, $a_timeout, $opts, migrate)
+ }};
+}
+pub(crate) use setup_db;
+
+use std::{os::fd::AsRawFd, path::Path, time::Duration};
+
+use fs_err::OpenOptions;
+use sqlx::{
+ SqlitePool,
+ sqlite::{SqliteConnectOptions, SqlitePoolOptions},
+};
+use tracing::debug;
+
+/// Helper for `setup_db!`
+pub(crate) async fn setup_db_inner(
+ db_path: &Path,
+ acquire_timeout: f64,
+ mk_opts: fn(&str) -> sqlx::Result<SqliteConnectOptions>,
+ migrate: impl AsyncFn(&SqlitePool) -> sqlx::Result<()>,
+) -> sqlx::Result<SqlitePool> {
+ async fn open_db(timeout: f64, opts: SqliteConnectOptions) -> sqlx::Result<SqlitePool> {
+ let pool = SqlitePoolOptions::new()
+ .acquire_timeout(Duration::from_secs_f64(timeout))
+ .connect_with(opts)
+ .await?;
+
+ Ok(pool)
+ }
+
+ {
+ let file = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(db_path)?;
+
+ // Lock the db file while we are running the migrations.
+ // Why? Because there is a small chance that we start running migrations (e.g. as the daemon)
+ // and then another process is started, which will also try to run migrations.
+ // Essentially, one of the processes will receive with a SQLite UNIQUE constraint failure.
+ // So let's avoid that possibility from the start.
+ file.lock()?;
+
+ let pool = open_db(
+ acquire_timeout,
+ mk_opts(format!("/proc/self/fd/{}", file.as_raw_fd()).as_str())?,
+ )
+ .await?;
+
+ debug!("running sqlite database setup");
+
+ migrate(&pool).await?;
+
+ file.unlock()?;
+ }
+
+ let real_opts = mk_opts(db_path.to_str().expect("Should be utf-8"))?;
+ let pool = open_db(acquire_timeout, real_opts).await?;
+
+ Ok(pool)
+}