Deserialize (potentially nested) environment variables into your custom structs

Overview

envious cover image

Bors enabled Crates.io Docs.rs

envious allows you to deserialize your serde enabled structs from environment variables.

See it in action:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
enum StaircaseOrientation {
    Left,
    Right,
}

#[derive(Serialize, Deserialize, Debug)]
struct Config {
    target_temp: f32,
    automate_doors: bool,

    staircase_orientation: StaircaseOrientation,
}

let config: Config = envious::from_env(envious::Prefix::None).expect("Could not deserialize from env");

With the following environment variables:

EXPORT target_temp=25.0
EXPORT automate_doors=true
EXPORT staircase_orientation=Left

it will parse it from the environment and give you a Rust struct you can use in your application.

Note: The environment variables are case sensitive! This is due to how serde works internally. If you want your structs to use SCREAMING_SNAKE_CASE, then be sure to use the #[serde(rename_all = "SCREAMING_SNAKE_CASE"] annotation on all concerned structs.

envious also supports the ability to only take in prefixed environment variables by passing in a envious::Prefix::Some("<prefix string>"). This will strip it before processing them further.

Getting Started

To use envious simply add it to your Cargo.toml with:

cargo add envious

and deserialize from your environment with envious::from_env!

⚠️ Current Shortcomings

  • Tuple Enum Variants can currently not be longer than one element!
  • Ordering of arrays is highly sensitive to environment order
    • No ordering is currently done, and the ordering depends on how the operating system propagates variables

How deserialization works

The mapping between environment variables and the serde model is as follows:

Nested fields are seperated by __ in their names

For example, if you have the following struct:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct Radiator {
    min_temp: f32,
    max_temp: f32,
}

#[derive(Serialize, Deserialize)]
struct Config {
    target_temp: f32,
    radiator: Option<Radiator>
}

You can deserialize Config with the following variables:

export target_temp=21.0
export radiator__min_temp=15.0
export radiator__max_temp=30.0

Arrays are serialized using nested fields with the individual keys being discarded

Arrays are represented as anonymous structs, with the 'fields' being the individual elements.

A more complex example could look like this:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
enum Material {
    Wood,
    Plastic,
}

#[derive(Serialize, Deserialize)]
struct Door {
    width: f32,
    height: f32,
    material: Material,
}

#[derive(Serialize, Deserialize)]
struct House {
    age: u32,
    entrance_doors: Vec<Door>,
}

Now, to deserialize a House we can set the following variables:

export age=120
export entrance_doors__0__width=100
export entrance_doors__0__height=100
export entrance_doors__0__material="Wood"
export entrance_doors__1__width=200
export entrance_doors__1__height=120
export entrance_doors__1__material="Plastic"
export entrance_doors__foo__width=400
export entrance_doors__foo__height=20
export entrance_doors__foo__material="Plastic"

As you can see, the individual 'keys' of the array do not matter! The same key refers to the same object though.

Unit enums variants (without fields), are serialized from strings

As you can see in the example above, the Material enum gets simply deserialized from the name of the variant. Be careful about upper/lower case Serde expects per-default that the case is exactly the same!

Complex enum variants are serialzed just like structs

Per default serde uses external tagging for more complicated enum variants. Tuple enums are currently only supported with a single value.

To see what this means, lets take this enum as an example:

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
enum Shape {
    Rectangle { width: f32, height: f32 },
    Circle(f32),
    Nothing,
}

#[derive(Serialize, Deserialize)]
struct Config {
    expected_shape: Shape,
}

To deserialize Config here, we can use the following variables:

export expected_shape__Rectangle__width=50.0
export expected_shape__Rectangle__height=10.0

// OR

export expected_shape__Circle=15.0

// OR

export expected_shape=Nothing

Any of these sets of variables would give you the expected outcome.

Should you change the tagging of your struct, be sure to adapt the given variables.

License

envious is licensed under MIT or Apache 2.0, as you wish.

Contributing

To contribute to envious you can:

  • Open up issues with ideas, remarks, bug reports, etc...
  • Fork and implement new features and send them in as pull requests
  • Leave it a Star and spread the word! ;)
You might also like...
Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json file and discord webhook.

minecraft-honeypot Proxies all incoming connections to a minecraft server of your choosing, while also logging all ping and login requests to a json f

Share files between devices using your Wi-Fi network.
Share files between devices using your Wi-Fi network.

swift_file Rust implementation of transferring files between devices over Wi-Fi network using a QR code. Tool is inspired by claudiodangelis/qrcp. How

Axum + JWT authentication Middleware that allows you to start building your application fast

axum_jwt_ware Integration Guide Simple Axum + JWT authentication middleware with implemented Login and refresh token. Goal I aim to simplify the proce

Loads environment variables into your structs in one shot.
Loads environment variables into your structs in one shot.

econf Loads environment variables into your structs in one shot. econf allows to override struct fields with environment variables easily. This is use

Macros for redis-rs to serialize and deserialize structs automatically

redis-macros Simple macros and wrappers to redis-rs to automatically serialize and deserialize structs with serde. Installation To install it, simply

vault client using jwt authentication that define environment variables from vault secrets before executing into something else

envlt envlt, like env, allows you to define environment variables and then execute into something else, but instead of static values, it uses using si

The [cain!] macro is a macro that rewrites sequential Rust branch statements into nested branches

Note! This crate is experimental and under development. It may include bugs that alter the behavior of your code in unexpected ways. You should review

Decode SCALE bytes into custom types using a scale-info type registry and a custom Visitor impl.

scale-decode This crate attempts to simplify the process of decoding SCALE encoded bytes into a custom data structure given a type registry (from scal

Build a config structure from environment variables in Rust without boilerplate

Yasec Yet another stupid environment config (YASEC) creates settings from environment variables. (Envconig-rs fork) Features Nested configuration stru

A Rust-based Garry's Mod module for fetching environment variables.

gm_environ Using Environment Variables in Garry's Mod. Installation Download a copy of the module from the releases (or compile from source) Move the

Envful is a CLI tool that verifies the presence of environment variables
Envful is a CLI tool that verifies the presence of environment variables

Envful is a CLI tool that verifies the presence of environment variables. It looks inside your .env file and the host system. You can use it to run any process while ensuring all the variables are set.

Set Shell Environment Variables across multiple shells with a single configuration file.

Xshe – Cross-Shell Environment Vars xshe allows for setting Shell Environment Variables across multiple shells with a single TOML configuration file.

Rust Lambda Extension for any Runtime to preload SSM Parameters as  🔐 Secure Environment Variables!
Rust Lambda Extension for any Runtime to preload SSM Parameters as 🔐 Secure Environment Variables!

🛡 Crypteia Rust Lambda Extension for any Runtime to preload SSM Parameters as Secure Environment Variables! Super fast and only performaned once duri

A Modern And Secure CLI Tool For Managing Environment Variables
A Modern And Secure CLI Tool For Managing Environment Variables

Envio is a command-line tool that simplifies the management of environment variables across multiple profiles. It allows users to easily switch between different configurations and apply them to their current environment

A tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding.

dts A simple tool to deserialize data from an input encoding, transform it and serialize it back into an output encoding. Requires rust = 1.56.0. Ins

Catch Tailwindcss Errors  at Compile-Time Before They Catch You, without making any change to your code!  Supports overriding, extending, custom classes, custom modifiers, Plugins and many more 🚀🔥🦀
Catch Tailwindcss Errors at Compile-Time Before They Catch You, without making any change to your code! Supports overriding, extending, custom classes, custom modifiers, Plugins and many more 🚀🔥🦀

twust Twust is a powerful static checker in rust for TailwindCSS class names at compile-time. Table of Contents Overview Installation Usage Statement

A Rust macro for writing nested loop expressions

loop_chain A Rust macro for writing nested loop expressions Usage | Examples | Docs Dependencies [dependencies] loop_chain = "0.1.1" Usage For express

Avoid double indirection in nested smart pointers.

Pierce Avoid double indirection in nested smart pointers. The Pierce stuct allows you to cache the deref result of doubly-nested smart pointers. Quick

Pass Rust strings to C with potentially not needing heap allocation

cfixed-string is used for passing Rust string to C with potentially not needing to do a heap allocation. A problem with using the standard library CSt

Comments
  • Document feature comparison with `envy`

    Document feature comparison with `envy`

    After seeing envious in TWIR's "crate of the week" segment, I immediately thought of another, very similarly named library that does "the same thing" and that I've been using for my pet projects for a while now: https://github.com/softprops/envy. And I imagine I'm not the only one. Can we have a doc section (maybe not even in README, a separate file is fine) with a feature comparison?

    documentation help wanted good first issue 
    opened by ypoluektovich 1
  • Shall it expose a function to deserialize from any iterator of string pairts, not just `std::env`?

    Shall it expose a function to deserialize from any iterator of string pairts, not just `std::env`?

    Apart from from_env, shall there be "IO-less" (and maybe no_std-friendly) variant that takes generic (String, String) IntoIterator instead of specifically real environment variables.

    This would make testing or e.g. using environment list loaded from a file easier.

    documentation help wanted good first issue 
    opened by vi 1
Releases(v0.1.1)
Owner
Marcel Müller
@rust-lang enthusiast / Hobby GameDev
Marcel Müller
Rust Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev.

Rust Embed Rust Custom Derive Macro which loads files into the rust binary at compile time during release and loads the file from the fs during dev. Y

Peter 1k Jan 5, 2023
Turn GitHub into an RSS reader

NotCraft::NotFeed An RSS reader running entirely from your GitHub repo. Free hosting on GitHub Pages. No ads. No third party tracking. No need for bac

NotCraft 22 Nov 30, 2022
ditch poetry for pip. converts pyproject.toml into requirements.txt.

Say No To Poetry Did you grow up using just pip or conda? Are you being forced to use poetry everywhere at work? They try to convince you poetry just

Jaivarsan 7 Nov 6, 2022
Tools that parsing Rust code into UML diagram (in dot format currently).

rudg Rust UML Diagram Generator Tools that parsing Rust code into UML diagram (in dot format currently). Usage $ rudg.exe --help rudg 0.1.0 USAGE:

Zhai Yao 16 Nov 13, 2022
Merge multiple Juniper object definitions into a single object type.

juniper-compose Merge multiple Juniper object definitions into a single object type. crates.io | docs | github Motivation You are building a GraphQL s

Kit Isaev 3 Aug 5, 2022
Turn any web page into a desktop app (but, lightweight <1MB)

Intro Turn any web page into a desktop app (but, lightweight <1MB) The bundle will be less than 2MB Demo: https://i.imgur.com/BLr03oF.mp4 Install carg

null 9 Dec 27, 2022
Oso is an open source policy engine for authorization that’s embedded in your application

Oso What is Oso? Oso is an open source policy engine for authorization that’s embedded in your application. It provides a declarative policy language

oso 2.8k Jan 4, 2023
Sentinel: The Sentinel of Your Microservices

Sentinel: The Sentinel of Your Microservices (WIP) Introduction As distributed systems become increasingly popular, the reliability between services i

Sentinel Group 74 Dec 26, 2022
Build, bundle & ship your Rust WASM application to the web.

Trunk Build, bundle & ship your Rust WASM application to the web. ”Pack your things, we’re going on an adventure!” ~ Ferris Trunk is a WASM web applic

Anthony Dodd 2.2k Jan 7, 2023
Search Confluence from Alfred and open results in your browser.

Alfred Confluence Workflow Search Confluence from Alfred and open results in your browser. Features Search Confluence from Alfred and open results in

Johan M. 26 Nov 7, 2022