|  | 
| 1 | 1 | use std::collections::HashMap; | 
| 2 |  | -use std::path::PathBuf; | 
|  | 2 | +use std::path::{Path, PathBuf}; | 
| 3 | 3 | use std::sync::{Arc, Mutex}; | 
| 4 | 4 | use std::{fs, io}; | 
| 5 | 5 | 
 | 
| @@ -74,6 +74,7 @@ struct Metadata { | 
| 74 | 74 |     manifest_dir: PathBuf, | 
| 75 | 75 |     offline: bool, | 
| 76 | 76 |     database_url: Option<String>, | 
|  | 77 | +    offline_dir: Option<String>, | 
| 77 | 78 |     workspace_root: Arc<Mutex<Option<PathBuf>>>, | 
| 78 | 79 | } | 
| 79 | 80 | 
 | 
| @@ -114,39 +115,21 @@ static METADATA: Lazy<Mutex<HashMap<String, Metadata>>> = Lazy::new(Default::def | 
| 114 | 115 | fn init_metadata(manifest_dir: &String) -> Metadata { | 
| 115 | 116 |     let manifest_dir: PathBuf = manifest_dir.into(); | 
| 116 | 117 | 
 | 
| 117 |  | -    // If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this, | 
| 118 |  | -    // otherwise fallback to default dotenv behaviour. | 
| 119 |  | -    let env_path = manifest_dir.join(".env"); | 
| 120 |  | - | 
| 121 |  | -    #[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))] | 
| 122 |  | -    let env_path = if env_path.exists() { | 
| 123 |  | -        // Load the new environment variables and override the old ones if necessary. | 
| 124 |  | -        let res = dotenvy::from_path_override(&env_path); | 
| 125 |  | -        if let Err(e) = res { | 
| 126 |  | -            panic!("failed to load environment from {env_path:?}, {e}"); | 
| 127 |  | -        } | 
| 128 |  | - | 
| 129 |  | -        Some(env_path) | 
| 130 |  | -    } else { | 
| 131 |  | -        dotenvy::dotenv_override().ok() | 
| 132 |  | -    }; | 
| 133 |  | - | 
| 134 |  | -    // tell the compiler to watch the `.env` for changes, if applicable | 
| 135 |  | -    #[cfg(procmacro2_semver_exempt)] | 
| 136 |  | -    if let Some(env_path) = env_path.as_ref().and_then(|path| path.to_str()) { | 
| 137 |  | -        proc_macro::tracked_path::path(env_path); | 
| 138 |  | -    } | 
|  | 118 | +    let (database_url, offline, offline_dir) = load_dot_env(&manifest_dir); | 
| 139 | 119 | 
 | 
| 140 | 120 |     let offline = env("SQLX_OFFLINE") | 
|  | 121 | +        .ok() | 
|  | 122 | +        .or(offline) | 
| 141 | 123 |         .map(|s| s.eq_ignore_ascii_case("true") || s == "1") | 
| 142 | 124 |         .unwrap_or(false); | 
| 143 | 125 | 
 | 
| 144 |  | -    let database_url = env("DATABASE_URL").ok(); | 
|  | 126 | +    let database_url = env("DATABASE_URL").ok().or(database_url); | 
| 145 | 127 | 
 | 
| 146 | 128 |     Metadata { | 
| 147 | 129 |         manifest_dir, | 
| 148 | 130 |         offline, | 
| 149 | 131 |         database_url, | 
|  | 132 | +        offline_dir, | 
| 150 | 133 |         workspace_root: Arc::new(Mutex::new(None)), | 
| 151 | 134 |     } | 
| 152 | 135 | } | 
| @@ -182,7 +165,7 @@ pub fn expand_input<'a>( | 
| 182 | 165 | 
 | 
| 183 | 166 |             // Check SQLX_OFFLINE_DIR, then local .sqlx, then workspace .sqlx. | 
| 184 | 167 |             let dirs = [ | 
| 185 |  | -                |_: &Metadata| env("SQLX_OFFLINE_DIR").ok().map(PathBuf::from), | 
|  | 168 | +                |meta: &Metadata| meta.offline_dir.as_deref().map(PathBuf::from), | 
| 186 | 169 |                 |meta: &Metadata| Some(meta.manifest_dir.join(".sqlx")), | 
| 187 | 170 |                 |meta: &Metadata| Some(meta.workspace_root().join(".sqlx")), | 
| 188 | 171 |             ]; | 
| @@ -402,3 +385,52 @@ fn env(name: &str) -> Result<String, std::env::VarError> { | 
| 402 | 385 |         std::env::var(name) | 
| 403 | 386 |     } | 
| 404 | 387 | } | 
|  | 388 | + | 
|  | 389 | +/// Get `DATABASE_URL`, `SQLX_OFFLINE` and `SQLX_OFFLINE_DIR` from the `.env`. | 
|  | 390 | +fn load_dot_env(manifest_dir: &Path) -> (Option<String>, Option<String>, Option<String>) { | 
|  | 391 | +    let mut env_path = manifest_dir.join(".env"); | 
|  | 392 | + | 
|  | 393 | +    // If a .env file exists at CARGO_MANIFEST_DIR, load environment variables from this, | 
|  | 394 | +    // otherwise fallback to default dotenv file. | 
|  | 395 | +    #[cfg_attr(not(procmacro2_semver_exempt), allow(unused_variables))] | 
|  | 396 | +    let env_file = if env_path.exists() { | 
|  | 397 | +        let res = dotenvy::from_path_iter(&env_path); | 
|  | 398 | +        match res { | 
|  | 399 | +            Ok(iter) => Some(iter), | 
|  | 400 | +            Err(e) => panic!("failed to load environment from {env_path:?}, {e}"), | 
|  | 401 | +        } | 
|  | 402 | +    } else { | 
|  | 403 | +        #[allow(unused_assignments)] | 
|  | 404 | +        { | 
|  | 405 | +            env_path = PathBuf::from(".env"); | 
|  | 406 | +        } | 
|  | 407 | +        dotenvy::dotenv_iter().ok() | 
|  | 408 | +    }; | 
|  | 409 | + | 
|  | 410 | +    let mut offline = None; | 
|  | 411 | +    let mut database_url = None; | 
|  | 412 | +    let mut offline_dir = None; | 
|  | 413 | + | 
|  | 414 | +    if let Some(env_file) = env_file { | 
|  | 415 | +        // tell the compiler to watch the `.env` for changes. | 
|  | 416 | +        #[cfg(procmacro2_semver_exempt)] | 
|  | 417 | +        if let Some(env_path) = env_path.to_str() { | 
|  | 418 | +            proc_macro::tracked_path::path(env_path); | 
|  | 419 | +        } | 
|  | 420 | + | 
|  | 421 | +        for item in env_file { | 
|  | 422 | +            let Ok((key, value)) = item else { | 
|  | 423 | +                continue; | 
|  | 424 | +            }; | 
|  | 425 | + | 
|  | 426 | +            match key.as_str() { | 
|  | 427 | +                "DATABASE_URL" => database_url = Some(value), | 
|  | 428 | +                "SQLX_OFFLINE" => offline = Some(value), | 
|  | 429 | +                "SQLX_OFFLINE_DIR" => offline_dir = Some(value), | 
|  | 430 | +                _ => {} | 
|  | 431 | +            }; | 
|  | 432 | +        } | 
|  | 433 | +    } | 
|  | 434 | + | 
|  | 435 | +    (database_url, offline, offline_dir) | 
|  | 436 | +} | 
0 commit comments