toml-env

A simple configuration library using toml.

This library is designed to load a configuration for an application at startup using the initialize() function. The configuration can be loaded (in order of preference):

  1. From a dotenv style file .env.toml (a file name of your choosing)
  2. From an environment variable CONFIG (or a variable name of your choosing).
  3. From mapped environments (e.g. MY_VARIABLE => my_variable.child).
  4. From a configuration file.

Why yet another config library?

Here are some possible alternatives to this library:

Why would you use this one?

Config Struct

Firstly you need to define your struct which implements serde::de::DeserializeOwned + serde::Serialize + Default:

```rust

[derive(serde::Serialize, serde::Deserialize, Default)]

struct Config { configvalue1: String, configvalue2: String, config_child: ConfigChild }

[derive(serde::Serialize, serde::Deserialize, Default)]

struct ConfigChild { configvalue3: String, } ```

.env.toml

Initally configuration will attempted to be loaded from a file named .env.toml by default. You can elect to customize the name of this file. The format of this file is as follows:

```toml SECRETENVVAR1="some value" SECRETENVVAR2="some other value"

[CONFIG] configvalue1="some value" configvalue2="some other value"

[CONFIG.configchild] configvalue_3="some other other value" ```

Environment variables for the application can be set using the top level keys in the file (e.g. SECRET_ENV_VAR_1).

The configuration can be loaded from a subset of this file in CONFIG. The CONFIG key will be the name from the Args::config_variable_name which is CONFIG by default.

Environment Variable CONFIG

You can specify the configuration by storing it in the variable name as specified using Args::config_variable_name (CONFIG by default).

```bash

Store a multiline string into an environment variable in bash shell.

read -r -d '' CONFIG << EOM configvalue1="some value" configvalue2="some other value"

[configchild] configvalue_3="some other other value" EOM ```

Example

CONFIG Variable

A simple example loading configuration from CONFIG, using the default settings.

```rust use serde::{Deserialize, Serialize}; use toml_env::{initialize, Args};

[derive(Serialize, Deserialize)]

struct Config { value1: String, value2: bool, }

// Normally you may choose set this from a shell script or some // other source in your environment (docker file or server config file). std::env::setvar( "CONFIG", r#" value1="Something from CONFIG environment" value_2=true "#, );

let config: Config = initialize(Args::default()) .unwrap() .unwrap();

asserteq!(config.value1, "Something from CONFIG environment"); asserteq!(config.value2, true); ```

Custom Variable Mappings

A simple demonstration of the custom environment variable mappings:

```rust use serde::{Deserialize, Serialize}; use toml_env::{Args, initialize, TomlKeyPath}; use std::str::FromStr;

[derive(Serialize, Deserialize)]

struct Config { value1: String, value2: bool, }

// Normally you may choose set this from a shell script or some // other source in your environment (docker file or server config file). std::env::setvar("VALUE1", "Hello World"); std::env::setvar("VALUE2", "true");

let config: Config = initialize(Args { mapenv: [ ("VALUE1", "value1"), ("VALUE2", "value2"), ] .intoiter() .map(|(key, value)| { (key, TomlKeyPath::from_str(value).unwrap()) }).collect(), ..Args::default() }) .unwrap() .unwrap();

asserteq!(config.value1, "Hello World"); asserteq!(config.value2, true); ```

Automatic Variable Mappings

A simple demonstration of the automatic environment variable mappings:

```rust use serde::{Deserialize, Serialize}; use toml_env::{Args, initialize, AutoMapEnvArgs};

// NOTE: the deny_unknown_fields can be used to reject // mappings which don't conform to the current spec.

[derive(Serialize, Deserialize)]

[serde(denyunknownfields)]

struct Config { value1: String, value2: bool, }

// Normally you may choose set this from a shell script or some // other source in your environment (docker file or server config file). std::env::setvar("CONFIGVALUE1", "Hello World"); std::env::setvar("CONFIGVALUE2", "true");

let config: Config = initialize(Args { automapenv: Some(AutoMapEnvArgs::default()), // The default prefix is CONFIG. // In practice you would usually use a custom prefix: // prefix: Some("MY_APP"), ..Args::default() }) .unwrap() .unwrap();

asserteq!(config.value1, "Hello World"); asserteq!(config.value2, true); ```

.env.toml File

A simple example loading configuration and environment variables from .env.toml, using the default settings.

```rust use serde::{Deserialize, Serialize}; use toml_env::{Args, initialize};

[derive(Serialize, Deserialize)]

struct Config { value1: String, value2: bool, }

let dir = tempfile::tempdir().unwrap(); std::env::setcurrentdir(&dir).unwrap(); let dotenv_path = dir.path().join(".env.toml");

// Normally you would read this from .env.toml file std::fs::write( &dotenvpath, r#" OTHERVARIABLE="hello-world" [CONFIG] value1="Something from .env.toml" value2=true "#, ) .unwrap();

let config: Config = initialize(Args::default()) .unwrap() .unwrap();

asserteq!(config.value1, "Something from .env.toml"); asserteq!(config.value2, true);

let secret = std::env::var("OTHERVARIABLE").unwrap(); asserteq!(secret, "hello-world"); ```

All Features

A more complex example demonstrating all the features.

```rust use serde::{Deserialize, Serialize}; use tempfile::tempdir; use toml_env::{Args, initialize, Logging, TomlKeyPath, AutoMapEnvArgs}; use std::str::FromStr;

[derive(Serialize, Deserialize)]

[serde(denyunknownfields)]

struct Config { value1: String, value2: bool, child: Child, }

[derive(Serialize, Deserialize, Default)]

[serde(denyunknownfields)]

struct Child { value3: i32, value4: u8, value5: String, value6: String, }

let dir = tempdir().unwrap(); let dotenvpath = dir.path().join(".env.toml"); let configpath = dir.path().join("config.toml");

// Normally you would read this from .env.toml file std::fs::write( &dotenvpath, r#" SECRET="hello-world" [MYCONFIG] value1="Something from .env.toml" [MYCONFIG.child] value3=-5 value4=16 "#, ) .unwrap();

// Normally you may choose set this from a shell script or some // other source in your environment (docker file or server config file). std::env::setvar( "MYCONFIG", r#" value1="Something from MYCONFIG environment" value_2=true "#, );

std::env::setvar( "VALUE1", "Something from Environment" ); std::env::setvar( "VALUE5", "Something from Environment" ); std::env::setvar( "MYAPPCHILDVALUE_6", "Something from Environment" );

// Normally you would read this from config.toml // (or whatever name you want) file. std::fs::write( &configpath, r#" value1="Something from config.toml" value2=false [child] value4=45 "#, ) .unwrap();

let config: Config = initialize(Args { dotenvpath: &dotenvpath, configpath: Some(&configpath), configvariablename: "MYCONFIG", logging: Logging::StdOut, mapenv: [ ("VALUE1", "value1"), ("VALUE5", "child.value5"), ("VALUE99", "does.not.exist"), ] .intoiter() .map(|(key, value)| { (key, TomlKeyPath::fromstr(value).unwrap()) }).collect(), automapenv: Some(AutoMapEnvArgs { divider: "", prefix: Some("MYAPP"), transform: Box::new(|name| name.to_lowercase()), }) }) .unwrap() .unwrap();

asserteq!(config.value1, "Something from .env.toml"); asserteq!(config.value2, true); asserteq!(config.child.value3, -5); asserteq!(config.child.value4, 16);

let secret = std::env::var("SECRET").unwrap(); assert_eq!(secret, "hello-world"); ```

Changelog

See CHANGELOG.md for an account of changes to this library.