Skip to content

Commit 530ee61

Browse files
Leo5878Leo5878JasonShin
authored
Add support for loading environment variables from .env file (#208)
* feat: support loading variables from .env file * Fix. Remove debug logs * Remove unused string * refactor: removed duplicate call to dotenv::var * Add CLI argument for custom .env file name and update help text * clippy: use next back * fmt * dotenv test * fix the test * update tests --------- Co-authored-by: Leo5878 <[email protected]> Co-authored-by: JasonShin <[email protected]>
1 parent 9b13239 commit 530ee61

File tree

6 files changed

+120
-16
lines changed

6 files changed

+120
-16
lines changed

src/common/cli.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,8 @@ pub struct Cli {
7474
/// log level to be used for the CLI debug > info > warning > error
7575
#[clap(value_enum, long)]
7676
pub log_level: Option<LogLevel>,
77+
78+
/// Dotfile name (example: .env or .env.dev) [default: .env or environment]
79+
#[clap(long)]
80+
pub env: Option<String>,
7781
}

src/common/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl Default for Config {
8787

8888
impl Config {
8989
pub fn new() -> Config {
90-
let dotenv = Dotenv::new();
90+
let dotenv = Dotenv::new(CLI_ARGS.env.clone());
9191

9292
let default_config_path = PathBuf::from_str(".sqlxrc.json").unwrap();
9393
let default_ignore_config_path = PathBuf::from_str(".sqlxignore").unwrap();

src/common/dotenv.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::common::types::DatabaseType;
2-
use std::env::var;
2+
use dotenv;
33

44
#[derive(Clone, Debug)]
55
pub struct Dotenv {
@@ -14,14 +14,22 @@ pub struct Dotenv {
1414

1515
impl Default for Dotenv {
1616
fn default() -> Self {
17-
Self::new()
17+
Self::new(None)
1818
}
1919
}
2020

2121
impl Dotenv {
22-
pub fn new() -> Dotenv {
22+
fn get_var(key: &str) -> Option<String> {
23+
dotenv::var(key).ok()
24+
}
25+
26+
pub fn new(path_to_dotenv: Option<String>) -> Dotenv {
27+
if let Some(value) = path_to_dotenv {
28+
dotenv::from_path(value).ok();
29+
}
30+
2331
Dotenv {
24-
db_type: match var("DB_TYPE").ok() {
32+
db_type: match Self::get_var("DB_TYPE") {
2533
None => None,
2634
Some(val) => {
2735
if val == "mysql" {
@@ -31,14 +39,12 @@ impl Dotenv {
3139
}
3240
}
3341
},
34-
db_user: var("DB_USER").ok(),
35-
db_host: var("DB_HOST").ok(),
36-
db_port: var("DB_PORT")
37-
.ok()
38-
.map(|val| val.parse::<u16>().expect("DB_PORT is not a valid integer")),
39-
db_pass: var("DB_PASS").ok(),
40-
db_name: var("DB_NAME").ok(),
41-
pg_search_path: var("PG_SEARCH_PATH").ok(),
42+
db_user: Self::get_var("DB_USER"),
43+
db_host: Self::get_var("DB_HOST"),
44+
db_port: Self::get_var("DB_PORT").map(|val| val.parse::<u16>().expect("DB_PORT is not a valid integer")),
45+
db_pass: Self::get_var("DB_PASS"),
46+
db_name: Self::get_var("DB_NAME"),
47+
pg_search_path: Self::get_var("PG_SEARCH_PATH"),
4248
}
4349
}
4450
}

src/core/postgres/pool.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ impl bb8::ManageConnection for PostgresConnectionManager {
2020
type Error = Error;
2121

2222
async fn connect(&self) -> Result<Client, Error> {
23-
let conn_url = self.conn_url.clone();
24-
2523
let (client, connection) =
2624
tokio_postgres::connect(&self.conn_url, NoTls)
2725
.await

src/ts_generator/generator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub fn get_query_name(sql: &SQL) -> Result<String> {
3333
.unwrap()
3434
.as_str()
3535
.split(':')
36-
.last()
36+
.next_back()
3737
.unwrap()
3838
.to_string();
3939

tests/dotenv.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#[cfg(test)]
2+
mod dotenv_test {
3+
use assert_cmd::Command;
4+
use std::fs::{self};
5+
use std::io::Write;
6+
use tempfile::tempdir;
7+
8+
#[test]
9+
fn loads_env_vars_from_dotenv_file() -> Result<(), Box<dyn std::error::Error>> {
10+
let temp_dir = tempdir()?;
11+
let dotenv_path = temp_dir.path().join(".env");
12+
13+
let dotenv_content = r#"
14+
DB_TYPE=postgres
15+
DB_HOST=127.0.0.1
16+
DB_PORT=54321
17+
DB_USER=postgres
18+
DB_PASS=postgres
19+
DB_NAME=postgres
20+
"#;
21+
fs::write(&dotenv_path, dotenv_content)?;
22+
23+
let sample_dir = temp_dir.path().join("sample");
24+
fs::create_dir_all(&sample_dir)?;
25+
fs::copy("tests/sample/sample.ts", sample_dir.join("sample.ts"))?;
26+
27+
let mut cmd = Command::cargo_bin("sqlx-ts")?;
28+
cmd
29+
.current_dir(temp_dir.path())
30+
.arg("--ext=ts")
31+
.arg("-g")
32+
.arg(sample_dir.to_str().unwrap());
33+
34+
cmd
35+
.assert()
36+
.success()
37+
.stdout(predicates::str::contains("No SQL errors detected!")); // adjust if your CLI errors differently
38+
39+
Ok(())
40+
}
41+
42+
#[test]
43+
fn loads_env_vars_from_dotenv_file_but_wrong_config() -> Result<(), Box<dyn std::error::Error>> {
44+
let temp_dir = tempdir()?;
45+
let dotenv_path = temp_dir.path().join(".env");
46+
47+
let dotenv_content = r#"
48+
DB_TYPE=postgres
49+
DB_HOST=127.0.0.1
50+
DB_PORT=54322
51+
DB_USER=postgres
52+
DB_PASS=postgres
53+
DB_NAME=postgres
54+
"#;
55+
fs::write(&dotenv_path, dotenv_content)?;
56+
57+
let sample_dir = temp_dir.path().join("sample");
58+
fs::create_dir_all(&sample_dir)?;
59+
fs::copy("tests/sample/sample.ts", sample_dir.join("sample.ts"))?;
60+
61+
let mut cmd = Command::cargo_bin("sqlx-ts")?;
62+
cmd
63+
.current_dir(temp_dir.path())
64+
.arg("--ext=ts")
65+
.arg("-g")
66+
.arg(sample_dir.to_str().unwrap());
67+
68+
cmd.assert().failure().stderr(predicates::str::contains(
69+
"Postgres database connection error: error connecting to server: Connection refused",
70+
)); // adjust if your CLI errors differently
71+
72+
Ok(())
73+
}
74+
75+
#[test]
76+
fn fails_if_dotenv_missing() -> Result<(), Box<dyn std::error::Error>> {
77+
let temp_dir = tempdir()?;
78+
79+
let sample_dir = temp_dir.path().join("sample");
80+
fs::create_dir_all(&sample_dir)?;
81+
fs::copy("tests/sample/sample.ts", sample_dir.join("sample.ts"))?;
82+
83+
let mut cmd = Command::cargo_bin("sqlx-ts")?;
84+
cmd
85+
.current_dir(temp_dir.path())
86+
.arg("--ext=ts")
87+
.arg("-g")
88+
.arg(sample_dir.to_str().unwrap());
89+
90+
cmd.assert().failure().stderr(predicates::str::contains(
91+
"Unable to retrieve a database type, please check your configuration and try again",
92+
));
93+
94+
Ok(())
95+
}
96+
}

0 commit comments

Comments
 (0)