#env-var #environment #env #configuration #macro

macro envconfig_derive

Build a config structure from environment variables without boilerplate

11 releases (breaking)

0.10.0 Jun 8, 2021
0.9.1 Oct 9, 2020
0.8.0 Mar 31, 2020
0.6.0 Dec 22, 2019
0.5.0 Sep 25, 2018

#140 in #env-var

Download history 34746/week @ 2023-12-06 32379/week @ 2023-12-13 27184/week @ 2023-12-20 5370/week @ 2023-12-27 21577/week @ 2024-01-03 20577/week @ 2024-01-10 24849/week @ 2024-01-17 29829/week @ 2024-01-24 22969/week @ 2024-01-31 28812/week @ 2024-02-07 24334/week @ 2024-02-14 23369/week @ 2024-02-21 32028/week @ 2024-02-28 31648/week @ 2024-03-06 33596/week @ 2024-03-13 17492/week @ 2024-03-20

120,949 downloads per month
Used in 16 crates (4 directly)

MIT license

13KB
209 lines

Envconfig logo

Build Status License Documentation

Initialize config structure from environment variables in Rust without boilerplate.

Usage

Let's say you application relies on the following environment variables:

  • DB_HOST
  • DB_PORT

And you want to initialize Config structure like this one:

struct Config {
    host: String,
    port: u16,
}

You can achieve this with the following code without boilerplate:

use envconfig::Envconfig;

#[derive(Envconfig)]
pub struct Config {
    #[envconfig(from = "DB_HOST")]
    pub db_host: String,

    #[envconfig(from = "DB_PORT", default = "5432")]
    pub db_port: u16,
}

fn main() {
    // Assuming the following environment variables are set
    std::env::set_var("DB_HOST", "127.0.0.1");

    // Initialize config from environment variables or terminate the process.
    let config = Config::init_from_env().unwrap();

    assert_eq!(config.db_host, "127.0.0.1");
    assert_eq!(config.db_port, 5432);
}

Testing

When writing tests you should avoid using environment variables. Cargo runs Rust tests in parallel by default which means you can end up with race conditions in your tests if two or more are fighting over an environment variable.

To solve this you can initialise your struct from a HashMap<String, String> in your tests. The HashMap should match what you expect the real environment variables to be; for example DB_HOST environment variable becomes a DB_HOST key in your HashMap.

use envconfig::Envconfig;

#[derive(Envconfig)]
pub struct Config {
    #[envconfig(from = "DB_HOST")]
    pub db_host: String,

    #[envconfig(from = "DB_PORT", default = "5432")]
    pub db_port: u16,
}

#[test]
fn test_config_can_be_loaded_from_hashmap() {
    // Create a HashMap that looks like your environment 
    let mut hashmap = HashMap::new();
    hashmap.insert("DB_HOST".to_string(), "127.0.0.1".to_string());

    // Initialize config from a HashMap to avoid test race conditions
    let config = Config::init_from_hashmap(&hashmap).unwrap();

    assert_eq!(config.db_host, "127.0.0.1");
    assert_eq!(config.db_port, 5432);
}

Contributing

Running tests

Tests do some manipulation with environment variables, so to prevent flaky tests they have to be executed in a single thread:

cargo test -- --test-threads=1

License

MIT © Sergey Potapov

Contributors

  • greyblake Potapov Sergey - creator, maintainer.
  • allevo Tommaso Allevi - support nested structures
  • hobofan Maximilian Goisser - update dependencies

Dependencies

~1.5MB
~33K SLoC