I have a side project I’ve been working for the past year that heavily relies on environment variable. It’s called anyline and it’s intended to be a drop-in replacement for GNU’s readline. You can find the source code here and my Ziggit posts about it here.
Before 0.16.x, I would handle environment variables by calling to std.process.getEnvVarOwned and retrieving the variables where ever needed. You can find an example here.
After 0.16.x, I see a few options. The first naive approach that I could think of is simply require my library’s users to pass each necessary environment variable to each respective function that may require them, i.e.
pub fn readHistory(io: std.Io, alloc: Allocator, file: std.Io.File) ReadHistoryError!void {
...
}
Note how the file parameter is now required and the user doesn’t the option to opt out. While this is functional, I don’t want to force that constraint on my (hypothetical) users. Instead, I wish they still have the option to say no.
My second less-naive thought was allow the user to pass an optional environment variable map, i.e.
pub fn readHistory(
io: std.Io,
alloc: Allocator,
maybe_environ_map: ?*std.process.Environ.Map,
) ReadHistoryError!void {
...
}
Then users could simply opt out by passing null. However, now the user has lost the ability to override the default with their own path. I could add both parameters, i.e.
pub fn readHistory(
io: std.Io,
alloc: Allocator,
maybe_absolute_path: ?[]const u8,
maybe_environ_map: ?*std.process.Environ.Map,
) ReadHistoryError!void {
...
}
But now the interfaces just feels clumsy. What happens if a user passes null for the maybe_absolute_path and the environ_map_maybe? Do they simply get a runtime error saying they misused the function? I’m a bit stumped on how to get the best of both worlds.
What are your guys’ thoughts?