diff --git a/sqlx-macros-core/src/query/mod.rs b/sqlx-macros-core/src/query/mod.rs index d2392e2d0e..a51137413e 100644 --- a/sqlx-macros-core/src/query/mod.rs +++ b/sqlx-macros-core/src/query/mod.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use std::{fs, io}; @@ -74,6 +74,7 @@ struct Metadata { manifest_dir: PathBuf, offline: bool, database_url: Option, + offline_dir: Option, workspace_root: Arc>>, } @@ -114,39 +115,21 @@ static METADATA: Lazy>> = Lazy::new(Default::def fn init_metadata(manifest_dir: &String) -> Metadata { let manifest_dir: PathBuf = manifest_dir.into(); - // If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this, - // otherwise fallback to default dotenv behaviour. - let env_path = manifest_dir.join(".env"); - - #[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))] - let env_path = if env_path.exists() { - // Load the new environment variables and override the old ones if necessary. - let res = dotenvy::from_path_override(&env_path); - if let Err(e) = res { - panic!("failed to load environment from {env_path:?}, {e}"); - } - - Some(env_path) - } else { - dotenvy::dotenv_override().ok() - }; - - // tell the compiler to watch the `.env` for changes, if applicable - #[cfg(procmacro2_semver_exempt)] - if let Some(env_path) = env_path.as_ref().and_then(|path| path.to_str()) { - proc_macro::tracked_path::path(env_path); - } + let (database_url, offline, offline_dir) = load_dot_env(&manifest_dir); let offline = env("SQLX_OFFLINE") + .ok() + .or(offline) .map(|s| s.eq_ignore_ascii_case("true") || s == "1") .unwrap_or(false); - let database_url = env("DATABASE_URL").ok(); + let database_url = env("DATABASE_URL").ok().or(database_url); Metadata { manifest_dir, offline, database_url, + offline_dir, workspace_root: Arc::new(Mutex::new(None)), } } @@ -182,7 +165,7 @@ pub fn expand_input<'a>( // Check SQLX_OFFLINE_DIR, then local .sqlx, then workspace .sqlx. let dirs = [ - |_: &Metadata| env("SQLX_OFFLINE_DIR").ok().map(PathBuf::from), + |meta: &Metadata| meta.offline_dir.as_deref().map(PathBuf::from), |meta: &Metadata| Some(meta.manifest_dir.join(".sqlx")), |meta: &Metadata| Some(meta.workspace_root().join(".sqlx")), ]; @@ -402,3 +385,52 @@ fn env(name: &str) -> Result { std::env::var(name) } } + +/// Get `DATABASE_URL`, `SQLX_OFFLINE` and `SQLX_OFFLINE_DIR` from the `.env`. +fn load_dot_env(manifest_dir: &Path) -> (Option, Option, Option) { + let mut env_path = manifest_dir.join(".env"); + + // If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this, + // otherwise fallback to default dotenv file. + #[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))] + let env_file = if env_path.exists() { + let res = dotenvy::from_path_iter(&env_path); + match res { + Ok(iter) => Some(iter), + Err(e) => panic!("failed to load environment from {env_path:?}, {e}"), + } + } else { + #[allow(unused_assignments)] + { + env_path = PathBuf::from(".env"); + } + dotenvy::dotenv_iter().ok() + }; + + let mut offline = None; + let mut database_url = None; + let mut offline_dir = None; + + if let Some(env_file) = env_file { + // tell the compiler to watch the `.env` for changes. + #[cfg(procmacro2_semver_exempt)] + if let Some(env_path) = env_path.to_str() { + proc_macro::tracked_path::path(env_path); + } + + for item in env_file { + let Ok((key, value)) = item else { + continue; + }; + + match key.as_str() { + "DATABASE_URL" => database_url = Some(value), + "SQLX_OFFLINE" => offline = Some(value), + "SQLX_OFFLINE_DIR" => offline_dir = Some(value), + _ => {} + }; + } + } + + (database_url, offline, offline_dir) +}