Skip to content

add fn to_vec for easily printing config values in any simple format #66

@JonasJebing

Description

@JonasJebing

💡 Feature description

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)>> { ... }

💻 Basic example

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="" 

Alternatives

Use std::fmt::Debug instead

This has the following downsides:

  • Fields aren't SCREAMING_SNAKE_CASE and it's not feasible to output a specific format with Debug.
  • If you have a password field or other secret, you can no longer use derive(Debug).
    With serde you could use #[serde(skip_serializing)].

Write a custom Serializer or fn to_custom_format for every format instead

I think for simple formats writing a Serializer is more complicated than a function using to_vec.

Add a fn to_iter instead

pub 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:?}");
}

Not adding anything to envy

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions