Skip to content

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

econf

CI Go Reference Go Rust Zig License: MIT

econf loads strongly typed configuration values from environment variables or from files referenced by environment variables. This repository contains three sibling implementations with one shared behavior spec:

  • go/: Go module
  • rust/: Rust workspace with runtime crate and derive macro
  • zig/: Zig package
  • spec/: shared behavior and conformance scenarios

Shared Behavior

For a config type and field, econf builds an environment variable name by converting both names to uppercase snake case and joining them with _.

MyConfig.Host       -> MY_CONFIG_HOST
MyConfig.KeyListNUM -> MY_CONFIG_KEY_LIST_NUM

Each field may be loaded from either:

MY_CONFIG_HOST=value
MY_CONFIG_HOST_FILE=/path/to/file-containing-value

When both are present, *_FILE wins. File contents are trimmed before parsing. Missing or empty values leave the field unchanged. Fields whose names start with _ are skipped.

Supported baseline type families are strings, booleans, signed integers, floats, and slices/vectors of those scalar types. Unsigned integers are intentionally outside the shared baseline.

All implementations accept the same boolean tokens: 1/0 exactly, plus case-insensitive t, true, y, yes, f, false, n, no. Anything else is a parse error.

Go

import econf "github.com/liuchong/econf/go"

type MyConfig struct {
    Host    string
    Port    int
    Enabled bool
    Names   []string
    _secret string
}

func main() {
    cfg := &MyConfig{}
    econf.SetFields(cfg)
}

Go parsing errors panic, matching the original package behavior. See go/README.md.

Rust

Published on crates.io as ec (easy ENV config):

cargo add ec
use ec::{load, Econf};

#[derive(Default, Econf)]
struct MyConfig {
    host: String,
    port: i32,
    enabled: bool,
    names: Vec<String>,
    _secret: String,
}

fn main() -> ec::Result<()> {
    let mut cfg = MyConfig::default();
    load(&mut cfg)?;
    Ok(())
}

Rust returns Result for parse and file errors. See rust/README.md.

Zig

const std = @import("std");
const econf = @import("econf");

const MyConfig = struct {
    host: []const u8 = "",
    port: i32 = 0,
    enabled: bool = false,
    names: []const []const u8 = &.{},
};

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var cfg = MyConfig{};
    try econf.load(&cfg, allocator, .{});
}

Zig returns errors and uses the caller's allocator for loaded strings and slices. See zig/README.md.

Conformance

The shared scenarios live in spec/cases/*.json and are executed directly by a conformance runner in each implementation, so adding or changing a case applies to Go, Rust, and Zig at once. Scenarios cover scalar fields, boolean tokens, file override, slices, skipped _ fields, custom separators, and parse errors.

Run the full suite:

./scripts/conformance.sh

See spec/conformance.md for the case schema and runner locations.

License

MIT

About

A simple configure tool use ENV or ENV_FILE

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages