envfmt: Provide Default Values

Provide Default Values

How to handle variables that might not be set by providing a fallback.

What happens if a variable isn’t set? If you use .unwrap(), your program will panic with a VariableNotFound error. To avoid this, you can provide a default value right in your template string.

The syntax for this is ${VAR:-default}. If VAR is missing from the environment or your HashMap, envfmt will use default instead.

Using Defaults with Environment Variables

Let’s say your app needs an RPC_URL, but you want it to default to a local node if the variable isn’t set.

You can write your template like this:

src/main.rs
fn main() {
    let template = "Using RPC URL: ${RPC_URL:-http://localhost:8545}";
    let rpc_url = envfmt::format(template).unwrap();
    println!("{}", rpc_url);
}

Now, if you run this without setting the RPC_URL variable:

Shell
cargo run

The output will be:

Plain Text
Using RPC URL: http://localhost:8545

If you do set it, your value will be used instead:

Shell
RPC_URL=https://mainnet.infura.io/v3/my-key cargo run

The output will be:

Plain Text
Using RPC URL: https://mainnet.infura.io/v3/my-key

Using Defaults with a HashMap

This works the exact same way with format_with() and a HashMap. If a key is missing from the map, the default value is used.

src/main.rs
use std::collections::HashMap;

fn main() {
    let mut context = HashMap::new();
    context.insert("CHAIN_ID", "1");

    // Note that "RPC_URL" is not in the context

    let template = "Connecting to ${RPC_URL:-http://localhost:8545} for chain $CHAIN_ID...";
    let connection_string = envfmt::format_with(template, &context).unwrap();
    println!("{}", connection_string);
}

Running this will print:

Plain Text
Connecting to http://localhost:8545 for chain 1...

What About Empty Variables?

It’s important to know that a default value is only used if the variable is not set. If the variable is set to an empty string, envfmt will use the empty string. This is the same way shells work.

Here’s an example:

src/main.rs
use std::collections::HashMap;

fn main() {
    let mut context = HashMap::new();
    context.insert("ALCHEMY_API_KEY", ""); // Set to an empty string

    let template = "Alchemy key: '${ALCHEMY_API_KEY:-not_provided}'";
    let message = envfmt::format_with(template, &context).unwrap();
    println!("{}", message);
}

The output will be:

Plain Text
Alchemy key: ''

The default value not_provided was ignored because the ALCHEMY_API_KEY key exists in the HashMap, even though its value is empty.