Use dotenvy_macro for compile-time dotenv inspection
maindotenvy_macro crate provides a macro that allows you to inspect and load environment variables from a .env file at compile time. This is a well-maintained fork of dotenv_codegen.repository·main·Indexed 22 days ago
https://github.com/allan2/dotenvyA 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.
dotenvy_macro crate provides a macro that allows you to inspect and load environment variables from a .env file at compile time. This is a well-maintained fork of dotenv_codegen.To use dotenvy in your Rust project, add it to your Cargo.toml.
Note: dotenvy is a well-maintained fork of the original dotenv crate and is the suggested alternative for addressing security advisory RUSTSEC-2021-0141.
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,
}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());
}dotenv! macro provided by the dotenvy_macro crate.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(())
}You can configure EnvLoader to read from specific files, strings, or readers, and define the precedence of variable loading using EnvSequence.
let loader1 = EnvLoader::with_path("./.env").sequence(EnvSequence::InputThenEnv);
let loader2 = EnvLoader::new(); // shorthand for loader1// from a string
let s = "HOST=foo\nPORT=3000";
let str_loader = EnvLoader::with_reader(Cursor::new(s));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);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.
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.Error::not_found() method returns true if the error is an Io error with a std::io::ErrorKind::NotFound kind. This is useful for gracefully handling cases where a .env file is missing.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.
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.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].
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.
This function returns an error if:
=) or a NUL character.let key = "HOME";
match dotenvy::var(key) {
Ok(val) => println!("{key}: {val:?}"),
Err(e) => println!("couldn't interpret {key}: {e}"),
}