dotenvy

repository·main·Indexed 22 days ago

https://github.com/allan2/dotenvy

A well-maintained fork of the dotenv crate for Rust, providing tools to load environment variables from .env files. It supports runtime loading via a non-modifying API (EnvLoader and EnvMap), process environment modification via the #[dotenvy::load] attribute macro, and compile-time loading using the dotenv! macro from the dotenvy_macro crate. It also includes a CLI tool to execute commands with environment variables loaded from a file.

Tokens
2.8K
Snippets
5
Records
18
Agent score
78%

What's inside dotenvy

  1. Configure loading priority with `EnvSequence`

    main

    EnvSequence determines the precedence when merging environment variables from the current process and an input source (like a .env file).

    | Variant | Behavior | |---|---|\n| EnvOnly | Inherit the existing environment without loading from input. | | EnvThenInput | Inherit existing environment, then load from input, overriding existing values. | | InputOnly | Load from input only. | | InputThenEnv | Load from input, then inherit existing environment. Existing values are not overwritten. |

    #[derive(Default, Debug, PartialEq, Eq, Clone)]
    pub enum EnvSequence {
        EnvOnly,
        EnvThenInput,
        InputOnly,
        #[default]
        InputThenEnv,
    }
  2. Use the Modifying API with the `load` attribute macro

    main

    If you need to modify the actual process environment (e.g., to pass variables to a child process), use the load attribute macro. This requires enabling the macros feature.

    To ensure thread safety, the macro expands to modify the environment before the async runtime (like tokio) starts, avoiding issues with std::env::set_var in multi-threaded contexts.

    #[dotenvy::load]
    #[tokio::main]
    async fn main() {
        println!("HOST={}", std::env::var("HOST").unwrap());
    }
  3. Use the Non-modifying API for runtime loading

    main

    The non-modifying API is the recommended approach for most use cases. It loads environment variables into a map without altering the actual process environment (std::env). This is useful for avoiding thread-safety issues with set_var and for managing multiple configuration sources.

    Use EnvLoader to construct a loader and call .load() to retrieve an environment map.

    use dotenvy::{EnvLoader};
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let env_map = EnvLoader::new().load()?;
        println!("HOST={}", env_map.var("HOST")?);
        Ok(())
    }
  4. Configure EnvLoader for different sources and sequences

    main

    You can configure EnvLoader to read from specific files, strings, or readers, and define the precedence of variable loading using EnvSequence.

    Loading from a file

    let loader1 = EnvLoader::with_path("./.env").sequence(EnvSequence::InputThenEnv);
    let loader2 = EnvLoader::new();  // shorthand for loader1

    Loading from a string or reader

    // from a string
    let s = "HOST=foo\nPORT=3000";
    let str_loader = EnvLoader::with_reader(Cursor::new(s));

    Controlling precedence with EnvSequence

    Use .sequence() to determine if variables in the .env file should override existing environment variables or vice versa:

    • EnvSequence::InputThenEnv: Load from the file, then use existing environment variables.
    • EnvSequence::EnvThenInput: Load from the environment, then override with the file contents.
    • EnvSequence::EnvThenInput (implied by EnvLoader::new() default behavior in some contexts, but explicitly used to override existing values).
    // will load from the env file, override exiting values in the program environment
    let overriding_loader = EnvLoader::new().sequence(EnvSequence::EnvThenInput);
  5. Configure the `#[dotenvy::load]` attribute macro

    main

    The load attribute macro is configurable via arguments. The default configuration is: #[dotenvy::load(path = "./env", required = true, override_ = false)]

    For more advanced manual environment modification, use EnvLoader::load_and_modify.

  6. Initialize `EnvLoader`

    main

    EnvLoader can be initialized in several ways:

    • EnvLoader::new(): Sets the path to ./.env in the current directory.
    • EnvLoader::with_path(path): Sets the path to the provided value. This is infallible; IO is deferred until load is called.
    • EnvLoader::with_reader(reader): Sets a reader as the source. This is also infallible; IO is deferred.
    • .path(path): A builder method to set or override the path.
    • .sequence(sequence): A builder method to set the EnvSequence priority.
  7. Use the `#[dotenvy::load]` attribute macro for compile-time environment loading

    main

    The #[dotenvy::load] attribute macro allows you to load environment variables from a file at the very start of your application's execution. This is particularly useful for ensuring environment variables are present before any other logic or async runtimes start.

    Usage and Arguments

    You can pass three optional arguments to the macro:

    • path: The path to the environment file. Defaults to ./.env.
    • required: Whether the application should exit if the file is not found. Defaults to true.
    • override_: Whether to override existing environment variables with those found in the file. If true, it uses EnvSequence::InputOnly. If false, it uses EnvSequence::InputThenEnv (loading from the file only if the variable is not already set in the environment). Defaults to false.

    Async Runtime Compatibility

    When using an async runtime, the #[dotenvy::load] macro must be placed above async runtime spawning macros (such as #[tokio::main]). The macro works by wrapping your function in a synchronous wrapper that performs the loading before calling your original function (which is renamed with an _inner suffix).

    Note: This implementation is compatible with #[tokio::main], but is not compatible with #[async_std::main].

  8. Fetch an environment variable with `dotenvy::var`

    main

    Use dotenvy::var(key) to fetch an environment variable from the current process. This is a wrapper around std::env::var that returns a dotenvy::Error instead of std::env::VarError, providing more descriptive error messages like NotPresent(String) which includes the name of the missing key.

    Errors

    This function returns an error if:

    • The environment variable isn't set.
    • The variable name contains an equal sign (=) or a NUL character.
    • The value is not valid Unicode.
    let key = "HOME";
    match dotenvy::var(key) {
        Ok(val) => println!("{key}: {val:?}"),
        Err(e) => println!("couldn't interpret {key}: {e}"),
    }