envfmt: Implement the Context Trait

Implement the Context Trait

A more advanced guide for when you want to use your own structs as a source for variables.

So far, we’ve used environment variables and HashMap. But what if your data is in a custom struct? You could convert it to a HashMap first, but that’s extra work and a bit messy.

A better way is to teach envfmt how to read from your struct directly. You can do this by implementing the Context trait. This is the key to making envfmt super flexible.

The Context Trait

The trait itself is really simple. It looks like this:

Rust
pub trait Context {
    fn get(&self, key: &str) -> Option<String>;
}

It only has one method, get. This method takes a variable name (like "RPC_URL") and should return Some(String) if the variable exists, or None if it doesn’t.

Example: A Custom Ethereum Config Struct

Let’s say you have a config struct for your Ethereum application that you load from a file.

src/main.rs
struct Config {
    network: String,
    chain_id: u64,
    api_key: String,
}

We want to use an instance of Config directly with format_with(). To do that, we need to implement Context for it.

src/main.rs
use envfmt::Context;

struct Config {
    network: String,
    chain_id: u64,
    api_key: String,
}

// Here's the implementation
impl Context for Config {
    fn get(&self, key: &str) -> Option<String> {
        match key {
            "NETWORK" => Some(self.network.clone()),
            "CHAIN_ID" => Some(self.chain_id.to_string()),
            "API_KEY" => Some(self.api_key.clone()),
            _ => None, // For any other key, we don't have a value
        }
    }
}

In the get method, we just use a match statement. If the key is one we know, we return its value wrapped in Some(). Notice that we have to convert the chain_id number to a String. If the key is unknown, we return None.

Putting It All Together

Now we can use our Config struct with format_with() just like we did with a HashMap.

src/main.rs
fn main() {
    let config = Config {
        network: "mainnet".to_string(),
        chain_id: 1,
        api_key: "my-secret-key".to_string(),
    };

    let template = "https://eth-${NETWORK}.g.alchemy.com/v2/$API_KEY (Chain ID: $CHAIN_ID)";
    let rpc_url = format_with(template, &config).unwrap();

    println!("{}", rpc_url);
}

When you run this, the output will be:

Plain Text
https://eth-mainnet.g.alchemy.com/v2/my-secret-key (Chain ID: 1)

And that’s it. By implementing one small trait, you can make envfmt work with any of your own data structures.