Skip to main content

Crate deser_env

Crate deser_env 

Source
Expand description

Environment variables for deser.

use deser::Deserialize;

#[derive(Debug, Deserialize)]
struct Config {
    name: String,
    debug: bool,
    server: Server,
}

#[derive(Debug, Deserialize)]
struct Server {
    port: u16,
    max_connections: Option<u32>,
}

// `deser_env::from_env::<Config>("APP_")` reads the environment of the
// process, `from_vars` reads the given variables
let config: Config = deser_env::from_vars("APP_", [
    ("APP_NAME", "shop"),
    ("APP_DEBUG", "yes"),
    ("APP_SERVER__PORT", "8080"),
    ("PATH", "/usr/bin"),
])
.unwrap();
assert_eq!(config.name, "shop");
assert!(config.debug);
assert_eq!(config.server.port, 8080);
assert_eq!(config.server.max_connections, None);

§Data Model

The variables with names that start with a prefix (like APP_) are a map. The prefix is removed and the rest of the name is split at the separator (__ by default, see DeserializerConfig::set_separator) into nested keys which are lowercased (see Case):

variablesdeser
APP_PORT=80{"port": "80"}
APP_MAX_CONNECTIONS=5{"max_connections": "5"}
APP_SERVER__PORT=80{"server": {"port": "80"}}
APP_HOSTS__0=a APP_HOSTS__1=b{"hosts": ["a", "b"]}
APP_HOSTS__1=a APP_HOSTS__3=b{"hosts": {"1": "a", "3": "b"}}
APP_FEATURES__NEW_UI=on{"features": {"new_ui": "on"}}

A single _ separates words in names, which is why nested keys are separated with two. Indexes that start at 0 and have no gaps are sequences, other indexes are map keys. Names that start or end with the separator or contain it twice in a row are taken as they are. A name that has a value and nested names (APP_DB=x and APP_DB__POOL=4) is an error. The order of the environment is arbitrary, the variables are sorted by name.

Everything in the environment is text, the type of a value is only known to the type it’s deserialized into. Keys and values are therefore passed on as lexical atoms which are parsed by the types they are delivered to: numbers parse them, strings take them as they are. Booleans accept true, yes, on and 1 and false, no, off and 0. Lexical atoms are retained when values are buffered, so flattened structs and internally tagged and untagged enums work as well.

§Empty Values

A variable that is set to the empty string is the empty value, like ?page= in a query string: it’s None for optionals of types that do not accept it (APP_PORT= is None for an Option<u16>) and Some("") for an Option<String>. Variables that switch something on by being set (like APP_VERBOSE=) use the Flag adapter which treats the empty value as true:

use deser::adapters::Flag;

#[derive(deser::Deserialize)]
struct Options {
    #[deser(as = Flag)]
    verbose: bool,
    port: Option<u16>,
}

let options: Options =
    deser_env::from_vars("APP_", [("APP_VERBOSE", ""), ("APP_PORT", "")])
        .unwrap();
assert!(options.verbose);
assert_eq!(options.port, None);

§Lists

Sequences can be given with indexes (APP_HOSTS__0) without changes to the types. A single variable (APP_HOSTS=a) is a list of one value and a list without variables is empty (the maps of the environment are multimaps, like query strings). Lists in a single variable (APP_HOSTS=a,b,c) use the Separated adapter, with TrimWhitespace to allow spaces (a, b, c). Both work with all formats, a configuration file can still give an array:

use deser::adapters::{Separated, TrimWhitespace};

#[derive(deser::Deserialize)]
struct Config {
    #[deser(as = Separated<',', TrimWhitespace>)]
    hosts: Vec<String>,
    ports: Vec<u16>,
    tags: Vec<String>,
}

let config: Config = deser_env::from_vars("APP_", [
    ("APP_HOSTS", "a, b"),
    ("APP_PORTS__0", "80"),
    ("APP_PORTS__1", "443"),
])
.unwrap();
assert_eq!(config.hosts, ["a", "b"]);
assert_eq!(config.ports, [80, 443]);
assert!(config.tags.is_empty());

§Errors

Errors carry the name of the variable they refer to (see EnvVar). This is also the case for unknown fields that are collected as warnings (see UnknownFields) and for values which are buffered:

use deser_env::EnvVar;

#[derive(Debug, deser::Deserialize)]
struct Config {
    port: u16,
}

let vars = [("APP_PORT", "http")];
let err = deser_env::from_vars::<Config, _, _, _>("APP_", vars)
    .unwrap_err();
assert_eq!(err.attachment::<EnvVar>().unwrap().name(), "APP_PORT");
assert_eq!(
    err.to_string(),
    "InvalidValue: invalid value \"http\", expected u16 \
     (environment variable APP_PORT)"
);

§Layering

Environment variables typically override a configuration file. With update the variables that are set are applied to an existing value, nested structs are merged:

use deser::de::Deserializer as _;

#[derive(Debug, deser::Deserialize)]
struct Config {
    server: Server,
}

#[derive(Debug, deser::Deserialize)]
struct Server {
    host: String,
    port: u16,
}

let mut config = Config {
    server: Server { host: "localhost".into(), port: 80 },
};
deser_env::Deserializer::from_vars("APP_", [("APP_SERVER__PORT", "8080")])
    .update(&mut config)
    .unwrap();
assert_eq!(config.server.host, "localhost");
assert_eq!(config.server.port, 8080);

§Platforms

On Unix values which are not valid unicode are passed on as bytes (which Vec<u8> and PathBuf accept), on other platforms they are an error. Names that are not valid unicode are skipped unless they start with the prefix. On Windows names are not case sensitive, the prefix is matched ignoring ASCII case.

Setting environment variables of the running process is unsafe (see std::env::set_var), from_vars is a better fit for tests.

§Serialization

Values are serialized into name-value pairs (see to_vars and SerializerConfig), for instance to pass a configuration to a child process with Command::envs.

§Features

  • speedups (enabled by default): has no effect yet, it exists so that all formats have it.

Structs§

Deserializer
Deserializes environment variables.
DeserializerConfig
Configures how environment variables are deserialized.
DeserializerConfigBuilder
Builds a DeserializerConfig.
EnvVar
The environment variable an error refers to.
SerializerConfig
Configures how values are serialized into environment variables.
SerializerConfigBuilder
Builds a SerializerConfig.

Enums§

Case
How the case of names maps onto keys.

Functions§

from_env
Deserializes a value from the environment variables with a prefix.
from_vars
Deserializes a value from the given variables with a prefix.
to_vars
Serializes a value into environment variables with a prefix.
var
Deserializes the value of a single environment variable.