use std::sync::LazyLock;
use tokio::runtime::Runtime;
use tokio_postgres::Client;
struct MacroConnection {
runtime: Runtime,
client: Client,
_conn_handle: tokio::task::JoinHandle<()>,
}
static MACRO_CONN: LazyLock<Result<MacroConnection, String>> = LazyLock::new(|| {
let database_url = std::env::var("BSQL_DATABASE_URL")
.or_else(|_| std::env::var("DATABASE_URL"))
.map_err(|_| {
"bsql: BSQL_DATABASE_URL or DATABASE_URL must be set for compile-time \
SQL validation. Set one of these environment variables to a PostgreSQL \
connection URL (e.g. postgres://user:pass@localhost/mydb)."
.to_string()
})?;
let rt = Runtime::new().map_err(|e| format!("bsql: failed to create tokio runtime: {e}"))?;
let mut pg_config: tokio_postgres::Config = database_url
.parse()
.map_err(|e| format!("bsql: invalid DATABASE_URL: {e}"))?;
pg_config.connect_timeout(std::time::Duration::from_secs(10));
let (client, connection) = rt
.block_on(pg_config.connect(tokio_postgres::NoTls))
.map_err(|e| {
format!(
"bsql: failed to connect to PostgreSQL at compile time: {e}. \
Check that BSQL_DATABASE_URL or DATABASE_URL is set correctly \
and the database is running."
)
})?;
let handle = rt.spawn(async move {
if let Err(e) = connection.await {
eprintln!("bsql: compile-time connection error: {e}");
}
});
Ok(MacroConnection {
runtime: rt,
client,
_conn_handle: handle,
})
});
pub fn with_connection<F, T>(f: F) -> Result<T, syn::Error>
where
F: FnOnce(&Runtime, &Client) -> Result<T, String>,
{
let conn = MACRO_CONN
.as_ref()
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
f(&conn.runtime, &conn.client)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn missing_env_var_produces_error() {
let result: Result<(), syn::Error> = with_connection(|_rt, _client| Ok(()));
let _ = result;
}
}