Skip to main content

pray_core/
dotenv.rs

1use std::collections::BTreeMap;
2use std::fs;
3use std::path::Path;
4
5pub fn load_dotenv_variables(project_root_hint: &Path) -> BTreeMap<String, String> {
6    let path = project_root_hint.join(".env");
7    if !path.is_file() {
8        return BTreeMap::new();
9    }
10    let text = match fs::read_to_string(&path) {
11        Ok(text) => text,
12        Err(_) => return BTreeMap::new(),
13    };
14    parse_dotenv_text(&text)
15}
16
17fn parse_dotenv_text(text: &str) -> BTreeMap<String, String> {
18    let mut variables = BTreeMap::new();
19    for line in text.lines() {
20        let trimmed = line.trim();
21        if trimmed.is_empty() || trimmed.starts_with('#') {
22            continue;
23        }
24        let assignment = trimmed.strip_prefix("export ").unwrap_or(trimmed);
25        let Some((key, value)) = assignment.split_once('=') else {
26            continue;
27        };
28        let key = key.trim();
29        if key.is_empty() {
30            continue;
31        }
32        variables.insert(key.to_string(), parse_dotenv_value(value.trim()));
33    }
34    variables
35}
36
37fn parse_dotenv_value(value: &str) -> String {
38    if value.len() >= 2 {
39        let bytes = value.as_bytes();
40        let quote = bytes[0];
41        if (quote == b'"' || quote == b'\'') && bytes[bytes.len() - 1] == quote {
42            return value[1..value.len() - 1].to_string();
43        }
44    }
45    value.to_string()
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn parses_common_dotenv_forms() {
54        let variables = parse_dotenv_text(
55            r#"
56# comment
57export PRAY_ENV=development
58PRAY_PATH="/tmp/project"
59PRAY_FILE_PATH='configs/Prayfile'
60"#,
61        );
62        assert_eq!(
63            variables.get("PRAY_ENV").map(String::as_str),
64            Some("development")
65        );
66        assert_eq!(
67            variables.get("PRAY_PATH").map(String::as_str),
68            Some("/tmp/project")
69        );
70        assert_eq!(
71            variables.get("PRAY_FILE_PATH").map(String::as_str),
72            Some("configs/Prayfile")
73        );
74    }
75}