use std::sync::Arc;
use libsql::{Builder, Connection};
use crate::{Error, Result};
pub const URL_ENV: &str = "TURSO_DATABASE_URL";
pub const TOKEN_ENV: &str = "TURSO_AUTH_TOKEN";
pub const SETUP_HINT: &str = "Create a database with `turso db create <name>`, then set \
TURSO_DATABASE_URL (`turso db show --url <name>`) and TURSO_AUTH_TOKEN \
(`turso db tokens create <name>`) in .env.";
#[derive(Clone)]
pub struct DatabaseConfig {
pub url: String,
pub auth_token: String,
}
impl std::fmt::Debug for DatabaseConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DatabaseConfig")
.field("url", &self.url)
.field("auth_token", &"<redacted>")
.finish()
}
}
impl DatabaseConfig {
pub fn from_env() -> Result<Self> {
let url = std::env::var(URL_ENV)
.ok()
.filter(|url| !url.trim().is_empty())
.ok_or(Error::MissingDatabaseUrl(URL_ENV))?;
let auth_token = std::env::var(TOKEN_ENV).unwrap_or_default();
Ok(Self { url, auth_token })
}
}
#[derive(Clone)]
pub struct Database {
inner: Arc<libsql::Database>,
url: Arc<str>,
}
impl std::fmt::Debug for Database {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Database").field("url", &self.url).finish()
}
}
impl Database {
pub async fn open(config: &DatabaseConfig) -> Result<Self> {
let inner = Builder::new_remote(config.url.clone(), config.auth_token.clone())
.build()
.await?;
Ok(Self {
inner: Arc::new(inner),
url: config.url.as_str().into(),
})
}
pub async fn from_env() -> Result<Self> {
Self::open(&DatabaseConfig::from_env()?).await
}
pub fn url(&self) -> &str {
&self.url
}
pub async fn connect(&self) -> Result<Connection, libsql::Error> {
let conn = self.inner.connect()?;
conn.execute("PRAGMA foreign_keys = ON", ()).await?;
Ok(conn)
}
}