envcast 1.0.0

Short, clear description of what the project does
Documentation
use std::collections::HashMap;

mod parser;
mod reader;

/// Reads environment variable values from a `.env` file.
///
/// This function attempts to read the contents of a `.env` file (using the default path or
/// a custom path specified via the `DOTENV_CONFIG_FILE` environment variable) and parses
/// the contents into a `HashMap<String, String>`.
///
/// # Returns
///
/// - `Some(HashMap<String, String>)` if the `.env` file exists and contains valid key-value pairs.
/// - `None` if the `.env` file does not exist or could not be read.
///
/// # Note
///
/// This function is used internally by the code generated by the `FromEnv` derive macro.
pub fn get_dotenv_file_values() -> Option<HashMap<String, String>> {
    reader::get_dotenv_file_content().map(|content| parser::parse_dotenv_vars(content.as_str()))
}

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

    #[test]
    fn test_get_dotenv_file_values_by_envvar_path() {
        env::set_var("DOTENV_CONFIG_PATH", "../tests/data/.env");

        let content = get_dotenv_file_values();
        assert!(content.is_some());
        let result = content.unwrap();
        assert_eq!(result["BASIC"], "basic");
        assert_eq!(result["AFTER_LINE"], "after_line");
        assert_eq!(result["EMPTY"], "");
        assert_eq!(result["EMPTY_SINGLE_QUOTES"], "");
        assert_eq!(result["EMPTY_DOUBLE_QUOTES"], "");
        assert_eq!(result["EMPTY_BACKTICKS"], "");
        assert_eq!(result["EMPTY_INLINE_COMMENTS"], "");
        assert_eq!(result["SINGLE_QUOTES"], "single_quotes");
        assert_eq!(result["SINGLE_QUOTES_SPACED"], "    single quotes    ");
        assert_eq!(result["DOUBLE_QUOTES"], "double_quotes");
        assert_eq!(result["DOUBLE_QUOTES_SPACED"], "    double quotes    ");
        assert_eq!(
            result["DOUBLE_QUOTES_INSIDE_SINGLE"],
            "double \"quotes\" work inside single quotes"
        );
        assert_eq!(
            result["DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET"],
            "{ port: $MONGOLAB_PORT}"
        );
        assert_eq!(
            result["SINGLE_QUOTES_INSIDE_DOUBLE"],
            "single 'quotes' work inside double quotes"
        );
        assert_eq!(
            result["BACKTICKS_INSIDE_SINGLE"],
            "`backticks` work inside single quotes"
        );
        assert_eq!(
            result["BACKTICKS_INSIDE_DOUBLE"],
            "`backticks` work inside double quotes"
        );
        assert_eq!(result["BACKTICKS"], "backticks");
        assert_eq!(result["BACKTICKS_SPACED"], "    backticks    ");
        assert_eq!(
            result["DOUBLE_QUOTES_INSIDE_BACKTICKS"],
            "double \"quotes\" work inside backticks"
        );
        assert_eq!(
            result["SINGLE_QUOTES_INSIDE_BACKTICKS"],
            "single 'quotes' work inside backticks"
        );
        assert_eq!(
            result["DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS"],
            "double \"quotes\" and single 'quotes' work inside backticks"
        );
        assert_eq!(result["EXPAND_NEWLINES"], "expand\nnew\nlines");
        assert_eq!(result["DONT_EXPAND_UNQUOTED"], "dontexpand\\nnewlines");
        assert_eq!(result["DONT_EXPAND_SQUOTED"], "dontexpand\\nnewlines");
        assert_eq!(result["INLINE_COMMENTS"], "inline comments");
        assert_eq!(
            result["INLINE_COMMENTS_SINGLE_QUOTES"],
            "inline comments outside of #singlequotes"
        );
        assert_eq!(
            result["INLINE_COMMENTS_DOUBLE_QUOTES"],
            "inline comments outside of #doublequotes"
        );
        assert_eq!(
            result["INLINE_COMMENTS_BACKTICKS"],
            "inline comments outside of #backticks"
        );
        assert_eq!(
            result["INLINE_COMMENTS_SPACE"],
            "inline comments start with a"
        );
        assert_eq!(result["EQUAL_SIGNS"], "equals==");
        assert_eq!(result["RETAIN_INNER_QUOTES"], "{\"foo\": \"bar\"}");
        assert_eq!(
            result["RETAIN_INNER_QUOTES_AS_STRING"],
            "{\"foo\": \"bar\"}"
        );
        assert_eq!(
            result["RETAIN_INNER_QUOTES_AS_BACKTICKS"],
            "{\"foo\": \"bar's\"}"
        );
        assert_eq!(result["TRIM_SPACE_FROM_UNQUOTED"], "some spaced out string");
        assert_eq!(result["USERNAME"], "therealnerdybeast@example.tld");
        assert_eq!(result["SPACED_KEY"], "parsed");
        assert_eq!(result["EMPTY_SPACED_KEY"], "");
        assert_eq!(
            result["MULTI_DOUBLE_QUOTED"],
            "THIS\nIS\nA\nMULTILINE\nSTRING"
        );
        assert_eq!(
            result["MULTI_SINGLE_QUOTED"],
            "THIS\nIS\nA\nMULTILINE\nSTRING"
        );
        assert_eq!(
            result["MULTI_BACKTICKED"],
            "THIS\nIS\nA\n\"MULTILINE'S\"\nSTRING"
        );
        assert_eq!(
            result["MULTI_PEM_DOUBLE_QUOTED"],
            concat!(
                "-----BEGIN PUBLIC KEY-----\n",
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNl1tL3QjKp3DZWM0T3u\n",
                "LgGJQwu9WqyzHKZ6WIA5T+7zPjO1L8l3S8k8YzBrfH4mqWOD1GBI8Yjq2L1ac3Y/\n",
                "bTdfHN8CmQr2iDJC0C6zY8YV93oZB3x0zC/LPbRYpF8f6OqX1lZj5vo2zJZy4fI/\n",
                "kKcI5jHYc8VJq+KCuRZrvn+3V+KuL9tF9v8ZgjF2PZbU+LsCy5Yqg1M8f5Jp5f6V\n",
                "u4QuUoobAgMBAAE=\n",
                "-----END PUBLIC KEY-----",
            )
        );
        assert_eq!(result.len(), 41);

        env::remove_var("DOTENV_CONFIG_PATH");
    }

    #[test]
    fn test_get_dotenv_file_values_in_cwd() {
        fs::copy("../tests/data/.env", ".env").expect("Failed to copy file");

        let content = get_dotenv_file_values();
        // make sure the file is deleted even if tests fail
        fs::remove_file(".env").expect("Failed to delete temp file");

        assert!(content.is_some());
        let result = content.unwrap();
        assert_eq!(result["BASIC"], "basic");
        assert_eq!(result["AFTER_LINE"], "after_line");
        assert_eq!(result["EMPTY"], "");
        assert_eq!(result["EMPTY_SINGLE_QUOTES"], "");
        assert_eq!(result["EMPTY_DOUBLE_QUOTES"], "");
        assert_eq!(result["EMPTY_BACKTICKS"], "");
        assert_eq!(result["EMPTY_INLINE_COMMENTS"], "");
        assert_eq!(result["SINGLE_QUOTES"], "single_quotes");
        assert_eq!(result["SINGLE_QUOTES_SPACED"], "    single quotes    ");
        assert_eq!(result["DOUBLE_QUOTES"], "double_quotes");
        assert_eq!(result["DOUBLE_QUOTES_SPACED"], "    double quotes    ");
        assert_eq!(
            result["DOUBLE_QUOTES_INSIDE_SINGLE"],
            "double \"quotes\" work inside single quotes"
        );
        assert_eq!(
            result["DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET"],
            "{ port: $MONGOLAB_PORT}"
        );
        assert_eq!(
            result["SINGLE_QUOTES_INSIDE_DOUBLE"],
            "single 'quotes' work inside double quotes"
        );
        assert_eq!(
            result["BACKTICKS_INSIDE_SINGLE"],
            "`backticks` work inside single quotes"
        );
        assert_eq!(
            result["BACKTICKS_INSIDE_DOUBLE"],
            "`backticks` work inside double quotes"
        );
        assert_eq!(result["BACKTICKS"], "backticks");
        assert_eq!(result["BACKTICKS_SPACED"], "    backticks    ");
        assert_eq!(
            result["DOUBLE_QUOTES_INSIDE_BACKTICKS"],
            "double \"quotes\" work inside backticks"
        );
        assert_eq!(
            result["SINGLE_QUOTES_INSIDE_BACKTICKS"],
            "single 'quotes' work inside backticks"
        );
        assert_eq!(
            result["DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS"],
            "double \"quotes\" and single 'quotes' work inside backticks"
        );
        assert_eq!(result["EXPAND_NEWLINES"], "expand\nnew\nlines");
        assert_eq!(result["DONT_EXPAND_UNQUOTED"], "dontexpand\\nnewlines");
        assert_eq!(result["DONT_EXPAND_SQUOTED"], "dontexpand\\nnewlines");
        assert_eq!(result["INLINE_COMMENTS"], "inline comments");
        assert_eq!(
            result["INLINE_COMMENTS_SINGLE_QUOTES"],
            "inline comments outside of #singlequotes"
        );
        assert_eq!(
            result["INLINE_COMMENTS_DOUBLE_QUOTES"],
            "inline comments outside of #doublequotes"
        );
        assert_eq!(
            result["INLINE_COMMENTS_BACKTICKS"],
            "inline comments outside of #backticks"
        );
        assert_eq!(
            result["INLINE_COMMENTS_SPACE"],
            "inline comments start with a"
        );
        assert_eq!(result["EQUAL_SIGNS"], "equals==");
        assert_eq!(result["RETAIN_INNER_QUOTES"], "{\"foo\": \"bar\"}");
        assert_eq!(
            result["RETAIN_INNER_QUOTES_AS_STRING"],
            "{\"foo\": \"bar\"}"
        );
        assert_eq!(
            result["RETAIN_INNER_QUOTES_AS_BACKTICKS"],
            "{\"foo\": \"bar's\"}"
        );
        assert_eq!(result["TRIM_SPACE_FROM_UNQUOTED"], "some spaced out string");
        assert_eq!(result["USERNAME"], "therealnerdybeast@example.tld");
        assert_eq!(result["SPACED_KEY"], "parsed");
        assert_eq!(result["EMPTY_SPACED_KEY"], "");
        assert_eq!(
            result["MULTI_DOUBLE_QUOTED"],
            "THIS\nIS\nA\nMULTILINE\nSTRING"
        );
        assert_eq!(
            result["MULTI_SINGLE_QUOTED"],
            "THIS\nIS\nA\nMULTILINE\nSTRING"
        );
        assert_eq!(
            result["MULTI_BACKTICKED"],
            "THIS\nIS\nA\n\"MULTILINE'S\"\nSTRING"
        );
        assert_eq!(
            result["MULTI_PEM_DOUBLE_QUOTED"],
            concat!(
                "-----BEGIN PUBLIC KEY-----\n",
                "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNl1tL3QjKp3DZWM0T3u\n",
                "LgGJQwu9WqyzHKZ6WIA5T+7zPjO1L8l3S8k8YzBrfH4mqWOD1GBI8Yjq2L1ac3Y/\n",
                "bTdfHN8CmQr2iDJC0C6zY8YV93oZB3x0zC/LPbRYpF8f6OqX1lZj5vo2zJZy4fI/\n",
                "kKcI5jHYc8VJq+KCuRZrvn+3V+KuL9tF9v8ZgjF2PZbU+LsCy5Yqg1M8f5Jp5f6V\n",
                "u4QuUoobAgMBAAE=\n",
                "-----END PUBLIC KEY-----",
            )
        );
        assert_eq!(result.len(), 41);
    }

    #[test]
    fn test_get_dotenv_file_values_is_none() {
        let content = get_dotenv_file_values();
        assert!(content.is_none());
    }
}