envy

repository·master·Indexed 21 days ago

https://github.com/softprops/envy

A Rust library for deserializing environment variables into typesafe structs using Serde. It supports mapping environment variables to struct fields, using prefixes to avoid collisions, and deserializing from iterators. Features include support for comma-separated values in Vec<T>, optional fields via Option<T>, and integration with Serde attributes for default values and renaming.

Tokens
1.9K
Snippets
7
Records
9
Agent score
75%

What's inside envy

  1. Deserialize environment variables into structs

    master

    Use envy::from_env::<T>() to deserialize environment variables into a typesafe struct.

    Key Behaviors:

    • Naming Convention: Envy maps struct fields to environment variables using their names in all uppercase letters. For example, a field named foo_bar maps to the environment variable FOO_BAR.
    • Optional Fields: Fields defined with the Option<T> type will successfully deserialize even if the corresponding environment variable is missing.
    • Collections: Vec<T> fields can be deserialized from comma-separated environment variable values.
    • Serde Integration: Since envy is built on serde, you can use any Serde attributes, such as #[serde(default = "path")], to provide default values for missing environment variables.
    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
      foo: u16,
      bar: bool,
      baz: String,
      boom: Option<u64>
    }
    
    fn main() {
        match envy::from_env::<Config>() {
           Ok(config) => println!("{:#?}", config),
           Err(error) => panic!("{:#?}", error)
        }
    }
  2. How collections and enums are handled in Envy

    master

    Envy provides special handling for certain types to make environment variable configuration intuitive:

    • Collections (Vec<T>): Use a comma-separated string in the environment variable. For example, MY_LIST=1,2,3 will deserialize into Vec<i32> containing [1, 2, 3]. An empty string MY_LIST= will result in an empty vector.
    • Enums: Unit variants can be used as values. If you have an enum Size { Small, Medium, Large } with #[serde(rename_all = "lowercase")], setting SIZE=medium will work.
    • Serde Modifiers: All standard Serde attributes like #[serde(default)], #[serde(rename = "...")], and #[serde(skip)] work as expected.
    use serde::Deserialize;
    
    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(rename_all = "lowercase")]
    pub enum Size {
        Small, Medium, Large,
    }
    
    #[derive(Deserialize, Debug)]
    struct Config {
        size: Size,
        items: Vec<String>,
    }
    
    // Environment: SIZE=small, ITEMS=a,b,c
  3. Use environment variable prefixes

    master

    To avoid collisions with other environment variables, you can use the envy::prefixed("PREFIX_") interface. This requires all environment variables to start with the specified prefix.

    For example, if you use envy::prefixed("APP_").from_env::<Config>(), a struct field foo will look for the environment variable APP_FOO.

    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
      foo: u16,
      bar: bool,
      baz: String,
      boom: Option<u64>
    }
    
    fn main() {
        // Expects variables like APP_FOO, APP_BAR, etc.
        match envy::prefixed("APP_").from_env::<Config>() {
           Ok(config) => println!("{:#?}", config),
           Err(error) => panic!("{:#?}", error)
        }
    }
  4. Handle environment variable deserialization errors

    master

    When using envy to deserialize environment variables into a type, deserialization may fail, returning an envy::Error. You can handle these errors by matching on the following variants:

    • Error::MissingValue(String): Occurs when a required environment variable is not present. The contained String represents the name of the missing field.
    • Error::Custom(String): Occurs when a custom error is encountered during the deserialization process (e.g., via a serde custom error implementation).
  5. Filter environment variables by prefix with `prefixed`

    master

    To deserialize only environment variables that start with a specific prefix, use envy::prefixed("PREFIX_"). When matching, the prefix is stripped from the key before attempting to map it to the struct fields.

    Example: If you use prefixed("APP_"), an environment variable APP_FOO=bar will be mapped to the struct field foo.

    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
        foo: u16,
    }
    
    // Expects environment variables like APP_FOO=123
    match envy::prefixed("APP_").from_env::<Config>() {
        Ok(config) => println!("{:#?}", config),
        Err(error) => eprintln!("{:#?}", error),
    }
  6. Use exact field names with `keep_names`

    master

    By default, envy converts environment variable names to lowercase to match struct fields. If your environment variables use specific casing (e.g., BaR=value) and you want to match them exactly using Serde's field names, use envy::keep_names().

    Note that this bypasses the default lowercase normalization.

    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
        #[serde(rename = "BaR")]
        bar: String,
    }
    
    // Expects environment variable 'BaR=value'
    match envy::keep_names().from_env::<Config>() {
        Ok(config) => println!("{:#?}", config),
        Err(error) => eprintln!("{:#?}", error),
    }
  7. Deserialize environment variables with `from_env`

    master

    Use envy::from_env::<T>() to deserialize the process's environment variables into a typesafe struct. Your struct must implement serde::Deserialize. By default, envy maps environment variable names to struct fields by converting the environment variable names to lowercase.

    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
        foo: u16,
        bar: bool,
        baz: String,
        boom: Option<u64>,
    }
    
    match envy::from_env::<Config>() {
        Ok(config) => println!("{:#?}", config),
        Err(error) => eprintln!("{:#?}", error),
    }
  8. Deserialize from an iterator with `from_iter`

    master

    If you have a collection of key-value pairs (as (String, String) tuples) that is not the actual process environment, use envy::from_iter(iter). This is useful for testing or custom data sources.

    use serde::Deserialize;
    
    #[derive(Deserialize, Debug)]
    struct Config {
        foo: u16,
    }
    
    let data = vec![
        ("FOO".to_string(), "42".to_string()),
    ];
    
    let config: Config = envy::from_iter(data).unwrap();