softprops / envy Goto Github PK
View Code? Open in Web Editor NEWdeserialize env vars into typesafe structs with rust
License: MIT License
deserialize env vars into typesafe structs with rust
License: MIT License
I can't find any documentation on how to pass map fields to be deserialized.
For example, given
use std::collections::HashMap;
use serde_derive::Deserialize;
#[derive(Debug, Default, Deserialize)]
struct Config {
services: HashMap<String, String>
}
envy::from_env::<Config>()
How does one actually pass values to services
?
I've tried SERVICES=a=test,b=test
, which results in Err(Custom("invalid type: string \"a=test,b=test\", expected a map"))
and SERVICES_A=test
which causes Err(MissingValue("services"))
Is there a way to do this?
envy version: 0.4.2
I am testing constructing a struct from different sets of environment variables using serde and envy. Each test begins by clearing the environment variables with std::env::remove_var()
and then setting new values with std::env::set_var()
. After the values are set, I print the currently set values with std::env::vars()
, which are displayed correctly. After that, I construct the struct with envy, print what values it has and finally assert_eq!
that they're correct. Using just a single test to test a certain set of values, it works every time, but using multiple tests to test different sets of environment variables causes the tests to fail randomly. This is because the values envy sets into the struct are not the ones the test just set, but the ones the previous test did. Since the order the tests are ran with cargo test
isn't determenistic, the tests fail randomly.
envy gets the proper environment variables and sets them accordingly in all tests.
use serde::Deserialize;
const PREFIX: &str = "TEST_";
#[derive(Debug, Deserialize)]
struct Config {
pub value_1: String,
pub value_2: String,
}
impl Config {
fn from_env() -> Config {
envy::prefixed(PREFIX).from_env::<Config>().unwrap()
}
}
fn dump_env_lines(prefix: &str) -> Vec<String> {
std::env::vars()
.filter_map(|(k, v)| {
if k.starts_with(prefix) {
Some(format!("{}={}", k, v))
} else {
None
}
})
.collect::<Vec<String>>()
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_1() {
env::remove_var("TEST_VALUE_1");
env::remove_var("TEST_VALUE_2");
env::set_var("TEST_VALUE_1", "Hello");
env::set_var("TEST_VALUE_2", "World");
println!("{:?}", dump_env_lines(PREFIX));
let config = Config::from_env();
println!("{:?}", config);
assert_eq!(config.value_1, "Hello");
assert_eq!(config.value_2, "World");
}
#[test]
fn test_2() {
env::remove_var("TEST_VALUE_1");
env::remove_var("TEST_VALUE_2");
env::set_var("TEST_VALUE_1", "Test");
env::set_var("TEST_VALUE_2", "Value");
println!("{:?}", dump_env_lines(PREFIX));
let config = Config::from_env();
println!("{:?}", config);
assert_eq!(config.value_1, "Test");
assert_eq!(config.value_2, "Value");
}
}
Example output of one failed test run:
---- tests::test_2 stdout ----
["TEST_VALUE_1=Test", "TEST_VALUE_2=Value"]
Config { value_1: "Hello", value_2: "World" }
thread 'tests::test_2' panicked at 'assertion failed: `(left == right)`
left: `"Hello"`,
right: `"Test"`', src/lib.rs:62:9
The output shows that the Config struct has the values Hello
and World
, which are the ones set in the previously ran test, instead of the values Test
and Value
the current test previously set.
rustc 1.45.0 (5c1f21c3b 2020-07-13)
(latest stable at the time of writing)cargo 1.45.0 (744bd1fbb 2020-06-15)
Envy ignores the newtype hint from Serde and fails to deserialize "newtype" structs.
Envy should deserialize newtype structs. It makes little sense in Envy's case to handle them as tuple struct with 1 item.
Just wanted to ask whether or not this crate is still being maintained or not.
If it isn't, that's fine - I'd simply like to know whether or not I should just fork the crate to fix the problems that I have. Please don't take this issue as pressure :)
FYI: should be easy enough to implement with https://doc.rust-lang.org/std/path/
With a struct that uses serde's deny_unknown_fields
container attribute using non-prefixed keys envy kind-of correctly fails with the error unknown field an_other_env_variable, expected one of ...
.
Arguably that pragma does not make sense for that case, but might be necessary for other (de)serializations.
It ignores the other environment variables.
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct Config {
size: Option<u32>,
}
fn main() {
match envy::from_env::<Config>() {
Ok(config) => println!("provided config.size {:?}", config.size),
Err(err) => println!("error parsing config from env: {}", err),
}
}
envy version: 0.4.1
When using serde to rename a struct field, the code compiles, however the code will not find the renamed variable, nor the original name's one.
envy uses the new name in order to successfully find the environment variable
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Config {
#[serde(rename = "DATABASE_URL")]
database_url: String
}
fn main() {
let config = envy::from_env::<Config>().unwrap();
dbg!(config);
}
Linux
envy version: 0.4.2
Definitely like what you have here.
@softprops I've been working on a very general configuration solution that has become a superset of this crate's functionality. I'm proposing using config
internally in envy
.
For an example, here is the example from the readme converted to use config
.
#[macro_use]
extern crate serde_derive;
extern crate config;
#[derive(Deserialize, Debug)]
struct Config {
foo: u16,
bar: bool,
baz: String,
boom: Option<u64>
}
fn main() {
let mut c = config::Config::new();
// Not possible to error in this case but the `merge` function is very generic
c.merge(config::Environment::new()).unwrap();
match c.try_into::<Config>() {
Ok(config) => println!("{:#?}", config),
Err(error) => panic!("{:#?}", error)
}
}
A couple things you'd gain from using config
under-the-hood of envy
:
config::Environment::with_prefix("SOME_APP")
thread 'main' panicked at 'missing field `baz`', src/main.rs:21:21
Given the following main.rs
:
#[derive(serde::Deserialize, Clone, Debug, serde::Serialize)]
pub struct Person {
#[serde(rename(deserialize = "surname"))]
pub surname: String,
#[serde(skip_serializing, rename(deserialize = "FIRST_NAME"))]
pub first_name: String,
}
fn main() -> anyhow::Result<()> {
println!("FIRST_NAME: {}", std::env::var("FIRST_NAME").unwrap());
println!("surname: {}", std::env::var("surname").unwrap());
let person = envy::from_env::<Person>().map_err(|e| {
anyhow::anyhow!(e).context("Could not read Person from environment")
})?;
println!("Person: {:?}", person);
Ok(())
}
it fails to read the FIRST_NAME
environment variable:
$ surname=smith FIRST_NAME=john cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/envy_bug`
FIRST_NAME: john
surname: smith
Error: Could not Person from environment
Caused by:
missing value for field FIRST_NAME
It should have been able to read the FIRST_NAME
environment variable.
Run the above code.
I used the following Cargo.toml
:
[package]
name = "envy_bug"
version = "0.1.0"
authors = ["hottomato4"]
edition = "2018"
publish = ["github"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1", features = [ "derive" ] }
envy = "0.4"
anyhow = "1"
and the following Cargo.lock
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "anyhow"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "envy"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
dependencies = [
"serde",
]
[[package]]
name = "envy_bug"
version = "0.1.0"
dependencies = [
"anyhow",
"envy",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "anyhow"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
[[package]]
name = "envy"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965"
dependencies = [
"serde",
]
[[package]]
name = "envy_bug"
version = "0.1.0"
dependencies = [
"anyhow",
"envy",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
Also:
$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home: /opt/rust
installed targets for active toolchain
--------------------------------------
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
active toolchain
----------------
stable-x86_64-unknown-linux-gnu (default)
rustc 1.52.1 (9bc8c42bb 2021-05-09)
$ cargo --version
cargo 1.52.0 (69767412a 2021-04-21)
This should just work as into iterator is implemented for all iterators and makes it more convenient for clients
When trying to deserialize an untagged enum from a string, deserialization fails.
Having a field logging_format
which is an enum should deserialize properly from the environment.
use std::default::Default;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default)]
pub logging_format: LoggingFormat,
}
#[derive(Debug, Deserialize)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum LoggingFormat {
Json,
Plain,
}
impl Default for LoggingFormat {
fn default() -> Self {
LoggingFormat::Json
}
}
I'm deserializing this with:
let config: Config = envy::from_env().unwrap();
I'm executing my program with:
LOGGING_FORMAT=json cargo run
And I'm seeing the following output:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("data did not match any variant of untagged enum LoggingFormat")', src/libcore/result.rs:997:5
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
at src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:39
1: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:70
2: std::panicking::default_hook::{{closure}}
at src/libstd/sys_common/backtrace.rs:58
at src/libstd/panicking.rs:200
3: std::panicking::default_hook
at src/libstd/panicking.rs:215
4: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:478
5: std::panicking::continue_panic_fmt
at src/libstd/panicking.rs:385
6: rust_begin_unwind
at src/libstd/panicking.rs:312
7: core::panicking::panic_fmt
at src/libcore/panicking.rs:85
8: core::result::unwrap_failed
at /rustc/2aa4c46cfdd726e97360c2734835aa3515e8c858/src/libcore/macros.rs:16
9: <core::result::Result<T, E>>::unwrap
at /rustc/2aa4c46cfdd726e97360c2734835aa3515e8c858/src/libcore/result.rs:798
10: api::main
at src/main.rs:9
11: std::rt::lang_start::{{closure}}
at /rustc/2aa4c46cfdd726e97360c2734835aa3515e8c858/src/libstd/rt.rs:64
12: std::panicking::try::do_call
at src/libstd/rt.rs:49
at src/libstd/panicking.rs:297
13: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:92
14: std::rt::lang_start_internal
at src/libstd/panicking.rs:276
at src/libstd/panic.rs:388
at src/libstd/rt.rs:48
15: std::rt::lang_start
at /rustc/2aa4c46cfdd726e97360c2734835aa3515e8c858/src/libstd/rt.rs:64
16: main
I have tried variants of the string value of the enum like JSON
, json
, Json
, etc.
#[derive(Deserialize, Debug)]
struct A {
u: i32,
y: String,
}
#[derive(Deserialize, Debug)]
struct B {
a: A,
}
Is possible to de-serialize above? I would expected to work this as A_U=5
A_Y="Test"
or A=U=5,y=Test
but it don't.
Sorry, if the answer is obvious, but I could not find an example in the documentation nor the tests for this crate.
What should the content of an environment variable be if I want to deserialize a Option<String>
? A non-existent variable results in None
, but how to get a Some(_)
?
When I use #[serde(rename_all = "camelCase")]
attribute, envy seems to unable to find target variable.
I tried another case such as SCREAMING_SNAKE_CASE
but it also fails.
#[derive(Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
struct Bar {
hoge_hoge: String
}
#[test]
fn deserialize_with_rename_all() {
let data = vec![(String::from("hogeHoge"), String::from("yey"))];
// it returns "Err(MissingValue("hogeHoge"))"
from_iter::<_, Bar>(data);
}
envy should find corresponding environmental variable.
#[derive(Deserialize, PartialEq, Debug)]
#[serde(rename_all = "camelCase")]
struct Bar {
hoge_hoge: String
}
#[test]
fn deserialize_with_rename_all() {
let data = vec![(String::from("hogeHoge"), String::from("yey"))];
// it returns "Err(MissingValue("hogeHoge"))"
from_iter::<_, Bar>(data);
}
macOS 10.15.6
rustc 1.47.0
envy version: 0.4.2
Parsing finishes with error Error::MissingValue
if I rename a field of parsed structure to a word consists of capitals only.
Should be parsed without errors.
Define structure for parsing
#[derive(Deserialize, Debug)]
pub struct DB {
#[serde(rename="DB_USER")]
pub user: String,
#[serde(rename="DB_PASS")]
pub pass: String,
#[serde(rename="DB_NAME")]
pub name: String,
#[serde(default="host", rename="DB_HOST")]
pub host: String,
#[serde(default="port", rename="DB_PORT")]
pub port: u16,
}
let db_config = envy::from_env::<settings::DB>().unwrap();
Make export DB_USER=postgres
before running, run and get panic value: MissingValue("DB_USER")
Archlinux
envy version:
0.4.0
So. I see this code in lib.rs
impl<Iter: Iterator<Item = (String, String)>> Iterator for Vars<Iter> {
type Item = (VarName, Val);
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
.map(|(k, v)| (VarName(k.to_lowercase()), Val(k, v)))
}
}
So I understand why the k
is got lowercased. I get the expected behavior if I remove this conversion but it's not a solution because I lose feature of automatic comparison of a field of parsed structure and an environment variable. So my question is how to determine if a field has Serde tag rename
? I can use the determining in order to prevent the conversion only for "renamed" fields.
First of all, thanks for the fantastic library!
I think it would've been great to support dotenv-like behavior in envy.
That Node library can read environment variables from either environment variables directly (like envy does) or via .env
file, which is super convenient during the local development, cause we can have .env.example
with some default values for the development environment.
As an example, I can provide my own pet project where I played around with envy to actually make it work as I want.
As you can see, in load_config
we, first of all, try to load env variables as usual with envy and if it fails, we try to load and parse .env
file into the environment variables and re-read them again. And only after that, the final fail happens. This way, I can run the project from CLion without any additional configuration and explicit default values in the code.
Of course, it could be re-implemented in a different way, if it was to become a part of envy.
Personally I would like to be able to easily print out my config in a bash format.
Then I could copy one of the config's env vars from my app's output, change the env var and restart my binary.
Others may want to output their config's env vars in a YAML-like syntax and change them in their kubernetes config.
To provide those use cases with a simple and straight-forward solution, I propose to add the following function to envy:
pub fn to_vec<T: Serialize>(value: &T) -> Result<Vec<(String, String)>> { ... }
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Default)]
struct Config {
foo: u16,
bar: String,
}
for (key, value) in envy::to_vec(&Config::default())? {
println!("{key}={value:?}");
}
// stdout:
// FOO="0"
// BAR=""
This has the following downsides:
derive(Debug)
.#[serde(skip_serializing)]
.fn to_custom_format
for every format insteadI think for simple formats writing a Serializer is more complicated than a function using to_vec.
fn to_iter
insteadpub fn to_iter<T: Serialize>(value: &T) -> impl Iterator<Item = Result<(String, String)>> + '_ { ... }
I really like this solution, because it is very flexible.
At the least this alternative should be a candidate to add in the future as well.
Implementing this function without using a std::vec::IntoIter
is a bit awkward because the Serialize trait does not seem like it's designed for this kind of step-wise serialization to an Iterator.
Printing an Iterator<Item = Result<(String, String)>>
similar to the example above is a bit more complicated:
for result in envy::to_iter(&Config::default())? {
let (key, value) = result?;
println!("{key}={value:?}");
}
or when ignoring Err values:
for (key, value) in envy::to_iter(&Config::default()).filter_map(Result::ok) {
println!("{key}={value:?}");
}
There's definitely an argument to keep envy as simple as possible.
I'm really curious, what other people think about this idea.
Maybe there are simpler solutions that I've overlooked.
Lets move away from kcov and put our money on tarpaulin
In config-rs, there's an option to define a separator
which allow users to structure their env vars. I think an example will illustrate better:
#[derive(Deserialize)]
struct Config {
database: Database
}
#[derive(Deserialize)]
struct Database {
name: String,
}
If we define a separator, let's say "__", we could create this Config
by setting DATABASE__NAME=foo
.
Would it be possible and desirable for envy
to include it?
It is possible to get the same behavior using the flatten
attribute from serde (as noticed in #15). The drawback is that it demands a good amount of boilerplate because we would have to serde's rename
every field in the nested struct if we wanted to use this separator idea.
Using the flatten attribute from serde almost works but breaks in the case of non string values in flattened structs. In this case config always parses size as a string. However, if I put the size attribute directly in Config then everything works.
The usize value in the flattened struct should parse
#[derive(Deserialize)]
struct Config {
#[serde(flatten)]
pub subconfig: Subconfig
}
#[derive(Deserialize)]
struct Subconfig {
pub size: usize
}
nightly-x86_64-unknown-linux-gnu (default)
rustc 1.33.0-nightly (a8a2a887d 2018-12-16)
envy version:
latest
For example if my app is named "barstool" I'll probably be looking for BARSTOOL_BAZ, BARSTOOL_FOO, etc.
Many times environmental variables are optional for my applications. It would be nice to be able to specify some defaults.
Hey @softprops,
I encountered the problem of error accuracy when working with envy
.
This issue seems vaguely relevant, but it's missing any description, so I decided to create a new one.
Basically given code like this
struct Env {
pub some_var: String
}
fn main() {
let _env = envy::prefixed("ABC_")
.from_env::<Env>().unwrap();
}
I'm getting an error like this:
MissingValue("some_var")
where I'd like to see an error like this:
MissingValue("ABC_SOME_VAR")
I was able to overcome this in my project by a quick hack:
panic!("Missing value: ABC_{}", err.to_string().split_whitespace().last().unwrap().to_uppercase());
but obviously it would be better if envy
just had nicer errors in the first place.
I would be happy to introduce a PR, improving the error messages and adjusting the behavior for prefixed variables. Let me know if you're interested in seeing this, is the proposed improvement acceptable, and I'll work on it.
move further away from the pip tool and use the tools built into github
Continue parsing for more missing required values (instead stopping at the first one).
This can provide more information to the user/devops/developer in order to setup all missing values at once (instead of one by one).
Today, this:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Config {
host: String,
port: String,
}
fn main() {
match envy::prefixed("APP_").from_env::<Config>() {
Ok(config) => println!("{:#?}", config),
Err(err) => {
eprintln!("error: {:#?}", err);
std::process::exit(1);
}
}
}
running with:
cargo run
outputs this:
error: MissingValue(
"host",
)
The desired behaviour would output this instead:
error: MissingValues(
[
"host", // Or even better: "APP_HOST", considering prefix
"port" // Or even better: "APP_PORT", considering prefix
]
)
The readme says that it supports parsing into Vec by separating the values by commas.
If I want to parse a variable as Vec<PathBuf>
where each path could contain commas, would it work by putting that path in single quotes?
E.g.
FOLDERS='C:\Foo,Bar',D:\Baz
resulting in vec![r"C:\Foo,Bar", r"D:\Baz"]
?
And if there is a space after the separating comma, it won't work, right?
According to #65 env files should work with combination from dotenvy, but they don't seem to for me.
env file:
.env
REQUIRED_SALEOR_VERSION=">=3.11.7<4"
SALEOR_APP_ID="dummy-saleor-app-rs"
APP_API_BASE_URL="http://localhost:8000/graphql/"
APL="redis"
APL_URL="redis://redis:6379/2"
LOG_LEVEL="DEBUG"
running:
config.rs
use crate::saleor::AplType;
use tracing::Level;
#[derive(Debug, Deserialize)]
#[serde(remote = "Level")]
pub enum LocalTracingLevel {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct Config {
pub required_saleor_version: String,
pub saleor_app_id: String,
pub app_api_base_url: String,
pub apl: AplType,
pub apl_url: String,
#[serde(with = "LocalTracingLevel")]
pub log_level: tracing::Level,
}
impl Config {
pub fn load() -> Result<Self, envy::Error> {
dotenvy::dotenv().unwrap();
for (key, value) in std::env::vars() {
println!("{key}: {value}");
}
let env = envy::from_env::<Config>();
dbg!(&env);
env
}
}
Error/stdout:
REQUIRED_SALEOR_VERSION: >=3.11.7<4
SALEOR_APP_ID: dummy-saleor-app-rs
APP_API_BASE_URL: http://localhost:8000/graphql/
APL: redis
APL_URL: redis://redis:6379/2
LOG_LEVEL: DEBUG
[src/config.rs:36:9] &env = Err(
MissingValue(
"REQUIRED_SALEOR_VERSION",
),
)
Error: missing value for field REQUIRED_SALEOR_VERSION
Describe your issue in detail.
now that rustfmt is stable we no longer need to be using rustfmt-preview in ci
envy version:
When trying to deserialize an empty environment variable into a Vec<String>
, envy
creates a vector with an empty string instead of an empty vector.
envy
should return an empty vector.
Run this example with VALUES=""
:
use serde::Deserialize;
#[derive(Deserialize)]
struct Config {
values: Vec<String>,
}
fn main() {
match envy::from_env::<Config>() {
Ok(config) => println!("provided values: {:?}", config.values),
Err(err) => println!("error parsing config from env: {}", err),
}
}
$ VALUES="" cargo run -q --example empty-vec
provided values: [""]
envy version: v0.4.1-2-geb2e88c
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.