pray-core 1.9.0

Core library for Prayfile, the package manager for the language placed before inference
Documentation
use std::collections::BTreeMap;
use std::fs;
use std::path::Path;

pub fn load_dotenv_variables(project_root_hint: &Path) -> BTreeMap<String, String> {
    let path = project_root_hint.join(".env");
    if !path.is_file() {
        return BTreeMap::new();
    }
    let text = match fs::read_to_string(&path) {
        Ok(text) => text,
        Err(_) => return BTreeMap::new(),
    };
    parse_dotenv_text(&text)
}

fn parse_dotenv_text(text: &str) -> BTreeMap<String, String> {
    let mut variables = BTreeMap::new();
    for line in text.lines() {
        let trimmed = line.trim();
        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }
        let assignment = trimmed.strip_prefix("export ").unwrap_or(trimmed);
        let Some((key, value)) = assignment.split_once('=') else {
            continue;
        };
        let key = key.trim();
        if key.is_empty() {
            continue;
        }
        variables.insert(key.to_string(), parse_dotenv_value(value.trim()));
    }
    variables
}

fn parse_dotenv_value(value: &str) -> String {
    if value.len() >= 2 {
        let bytes = value.as_bytes();
        let quote = bytes[0];
        if (quote == b'"' || quote == b'\'') && bytes[bytes.len() - 1] == quote {
            return value[1..value.len() - 1].to_string();
        }
    }
    value.to_string()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_common_dotenv_forms() {
        let variables = parse_dotenv_text(
            r#"
# comment
export PRAY_ENV=development
PRAY_PATH="/tmp/project"
PRAY_FILE_PATH='configs/Prayfile'
"#,
        );
        assert_eq!(
            variables.get("PRAY_ENV").map(String::as_str),
            Some("development")
        );
        assert_eq!(
            variables.get("PRAY_PATH").map(String::as_str),
            Some("/tmp/project")
        );
        assert_eq!(
            variables.get("PRAY_FILE_PATH").map(String::as_str),
            Some("configs/Prayfile")
        );
    }
}