dotenv Rust Library

repository·master·Indexed 20 days ago

https://github.com/dotenv-rs/dotenv

A Rust implementation of dotenv that loads environment variables from a .env file into an application's environment. It supports recursive upward search for .env files, variable substitution using $VARIABLE or ${VARIABLE} syntax, and provides a CLI tool for running commands with loaded variables. The library includes functions like dotenv::dotenv(), from_filename(), and from_path(), as well as the dotenv! macro via the dotenv_codegen crate for compile-time environment variables.

Tokens
4.1K
Snippets
18
Records
22
Agent score
70%

What's inside dotenv

  1. Variable substitution in .env files

    master

    You can reuse variables within your .env file using $VARIABLE or ${VARIABLE} syntax, similar to bash.

    Substitution Rules:

    • Non-existing values: Replaced with an empty string.
    • Variable Names: All letters following the $ symbol are treated as the variable name. For non-alphanumeric names, use curly braces ${VAR_NAME}.
    • Quotes: Double quotes ("$VAR") do not prevent substitution.
    • Escaping: To prevent substitution, use single quotes ('$VAR') or a backslash (\$VAR).
    • Precedence: Environment variables from the operating system are used during substitution and will always override local variables defined within the .env file.
    VAR=one
    VAR_2=two
    
    # Basic substitution
    RESULT=$VAR # value: 'one'
    
    # Using curly braces for non-alphanumeric names
    RESULT=${VAR_2} # value: 'two'
    
    # Escaping to prevent substitution
    RESULT='$VAR' # value: '$VAR'
    RESULT=\$VAR # value: '$VAR'
    
    # OS environment variables override local definitions
    # If $PATH is set in your shell, it will be used here
    RESULT=$PATH
  2. Load environment variables with dotenv::dotenv()

    master

    The most common way to use this library is to call dotenv::dotenv() at the start of your application. This loads environment variables from a .env file located in the current directory or any of its parent directories. Once loaded, these variables are merged with the existing OS environment variables and can be accessed using standard library methods like std::env::var or std::env::vars.

    extern crate dotenv;
    
    use dotenv::dotenv;
    use std::env;
    
    fn main() {
        // Load the .env file. .ok() is used to ignore errors if the file is missing.
        dotenv().ok();
    
        for (key, value) in env::vars() {
            println!("{}: {}", key, value);
        }
    }
  3. Use the dotenv! macro for compile-time environment variables

    master

    The dotenv_codegen crate provides the dotenv! macro. This macro behaves like the standard env! macro but attempts to load values from a .env file at compile time. This is useful for embedding configuration directly into your binary.

    // 1. Add dotenv_codegen to your Cargo.toml dependencies
    
    // 2. Add this to the top of your crate
    #[macro_use]
    extern crate dotenv_codegen;
    
    fn main() {
      // 3. Use the macro to fetch a value at compile time
      println!("{}", dotenv!("MEANING_OF_LIFE"));
    }
  4. How variable substitution works in `.env` files

    master

    Variable substitution allows you to reuse values within your .env file.

    Substitution Rules:

    • Syntax: Use $VARIABLE_NAME or ${VARIABLE_NAME}.
    • Precedence: When a variable is substituted, the parser first checks the system environment variables. If the variable is not found in the system environment, it looks for the variable in the substitution_data (values parsed earlier in the current .env file).
    • Undefined Variables: If a variable is not found in the system environment OR the current file, it is replaced with an empty string.
    • Escaping Substitution: To prevent substitution, escape the $ symbol using a backslash (\$) or use strong quotes (').
    • Recursive Substitution: Substitutions can be recursive (e.g., KEY1=${KEY2} where KEY2 is defined later or previously).
    # Example of substitution
    BASE_URL=https://api.example.com
    API_ENDPOINT=${BASE_URL}/v1
    
    # Example of system env override
    # If SYSTEM_VAR is set in your shell, it will be used instead of 'local_val'
    VAR=${SYSTEM_VAR:-local_val}
  5. How the file discovery algorithm works

    master

    The discovery process follows a recursive upward search pattern:

    1. It checks if the filename exists within the provided directory.
    2. If the path exists and is a file, it returns that path.
    3. If the file is not found, it moves to the parent() directory and repeats the process.
    4. The search continues until the file is found or the filesystem root is reached. If the root is reached without finding the file, it returns an io::ErrorKind::NotFound error.
  6. Rules for valid keys and values in `.env` files

    master

    When writing .env files, follow these parsing rules to avoid LineParse errors:

    Keys:

    • Must start with an ASCII alphabetic character or an underscore (_).
    • Can contain alphanumeric characters, underscores (_), or dots (.).
    • A dot . cannot be the first character of a key.

    Values:

    • Comments: A # character starts a comment. If a space or tab precedes the #, the parser treats the preceding content as the value. If there is no space (e.g., KEY=val#comment), the # is treated as part of the value unless it's a quoted string.
    • Whitespace: Trailing whitespace is ignored. Whitespace between the key and the = sign is permitted.
    • Escaping: Inside unquoted values, use \ to escape special characters like \n for a newline or \ for a literal space.
    • Unterminated Quotes: Values wrapped in ' or " must be closed. An unclosed quote will trigger a LineParse error.
  7. Load environment variables from a specific file or path

    master

    If you need more control than the default behavior, use the following methods to specify exactly which file to load:

    • from_filename(filename): Loads variables from a specific filename.
    • from_path(path): Loads variables from a specific file path.
  8. Format of a .env file

    master

    A .env file consists of key-value pairs. Lines can be commented out using #. You can optionally prefix lines with export to make the file compatible with shell sourcing.

    # a comment, will be ignored
    REDIS_ADDRESS=localhost:6379
    MEANING_OF_LIFE=42
    
    # Optional export prefix for shell sourcing
    export API_KEY=secret_value
  9. Handle parsing errors with `LineParse`

    master

    If the parser encounters an invalid line format, it returns an Error::LineParse. This error includes the original line content and the character position (pos) where the parsing failed.

    Common causes for LineParse errors:

    • Invalid Key: Starting a key with a dot . or a non-alphabetic character.
    • Unterminated Quotes: Opening a ' or " but not closing it before the end of the line.
    • Invalid Escapes: Using a backslash \ followed by a character that is not a recognized escape sequence (e.g., \f is invalid, but \n is valid).
    • Malformed Substitution: Using ${ without a closing }.
  10. Load environment variables from an iterator using `Iter::load`

    master

    The Iter::load method consumes an Iter instance and attempts to set environment variables in the current process. It iterates through the parsed key-value pairs and calls std::env::set_var for each pair, but only if the variable is not already set in the environment. This ensures that existing system environment variables take precedence over those defined in the .env file.

    // Assuming 'reader' is an object implementing std::io::Read (e.g., a File or Cursor)
    let iter = Iter::new(reader);
    iter.load()?;
  11. Iterate over parsed key-value pairs with `Iter`

    master

    The Iter struct implements the Iterator trait, allowing you to manually traverse the key-value pairs parsed from a reader. Each item yielded by the iterator is a Result<(String, String)>. This is useful if you want to inspect or transform the variables before applying them to the environment.

    for item in iter {
        let (key, value) = item?;
        println!("{}={}", key, value);
    }
  12. Load a specific filename with `dotenv::from_filename()`

    master

    Use dotenv::from_filename(filename) to search for a specific file (e.g., custom.env) in the current directory or its parents. This is useful when you use non-standard naming conventions for your environment files.

    use dotenv;
    
    dotenv::from_filename("custom.env").ok();