{"id":32398990,"url":"https://github.com/zig-utils/zig-config","last_synced_at":"2026-04-01T17:08:36.746Z","repository":{"id":320628793,"uuid":"1082788611","full_name":"zig-utils/zig-config","owner":"zig-utils","description":"A zero-dependency, smart configuration loader for Zig.","archived":false,"fork":false,"pushed_at":"2026-03-30T13:50:57.000Z","size":54,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T15:29:01.356Z","etag":null,"topics":["config","configuration-loader","json","jsonc","toml","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zig-utils.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-24T19:15:30.000Z","updated_at":"2026-03-30T13:51:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"1a137e1e-0a06-46ad-ac68-14e149703bfa","html_url":"https://github.com/zig-utils/zig-config","commit_stats":null,"previous_names":["zig-utils/zig-config"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/zig-utils/zig-config","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zig-utils%2Fzig-config","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zig-utils%2Fzig-config/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zig-utils%2Fzig-config/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zig-utils%2Fzig-config/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zig-utils","download_url":"https://codeload.github.com/zig-utils/zig-config/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zig-utils%2Fzig-config/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290538,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["config","configuration-loader","json","jsonc","toml","zig"],"created_at":"2025-10-25T08:15:49.464Z","updated_at":"2026-04-01T17:08:36.731Z","avatar_url":"https://github.com/zig-utils.png","language":"Zig","readme":"# ZigConfig\n\nA zero-dependency configuration loader for Zig 0.16+, inspired by [bunfig](https://github.com/stacksjs/bunfig).\n\n## Features\n\n- 🔍 **Multi-source loading** - Local files, home directory, environment variables, defaults\n- 🎯 **Type-aware env vars** - Automatic parsing of booleans, numbers, arrays, and JSON\n- 🔗 **Deep merging** - Three strategies: replace, concat, and smart object array merging\n- 🛡️ **Circular reference detection** - Prevents infinite loops during merge\n- 📁 **Multiple formats** - JSON and Zig files (extensible)\n- 🎨 **Simple API** - Clean, ergonomic interface\n\n## Installation\n\nClone zig-config into your project's dependency directory:\n\n```bash\ngit clone --depth 1 https://github.com/zig-utils/zig-config.git\n```\n\nThen add it as a module in your `build.zig`:\n\n```zig\nexe.root_module.addImport(\"zig-config\", b.addModule(\"zig-config\", .{\n    .root_source_file = b.path(\"path/to/zig-config/src/zig-config.zig\"),\n    .target = target,\n}));\n```\n\n## Quick Start\n\n```zig\nconst std = @import(\"std\");\nconst zig_config = @import(\"zig-config\");\n\n// Define your configuration structure with full type safety!\nconst AppConfig = struct {\n    port: u16 = 8080,\n    debug: bool = false,\n    database: struct {\n        host: []const u8 = \"localhost\",\n        port: u16 = 5432,\n    } = .{},\n};\n\npub fn main() !void {\n    var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n    defer _ = gpa.deinit();\n    const allocator = gpa.allocator();\n\n    // Load configuration with compile-time type checking\n    var config = try zig_config.loadConfig(AppConfig, allocator, .{\n        .name = \"myapp\",\n    });\n    defer config.deinit(allocator);\n\n    // Access values with full type safety - no runtime type checking needed!\n    const port: u16 = config.value.port;           // Compile-time type safe!\n    const debug: bool = config.value.debug;         // IDE autocomplete works!\n    const db_host = config.value.database.host;     // Nested structs supported!\n\n    std.debug.print(\"Server running on port {d}\\n\", .{port});\n}\n```\n\n## Configuration Sources\n\nZigConfig loads configuration from multiple sources with the following priority (highest to lowest):\n\n1. **Environment variables** (highest priority)\n2. **Local project file** (`./myapp.json`, `./config/myapp.json`, `./.config/myapp.json`)\n3. **Home directory** (`~/.config/myapp.json`)\n4. **Defaults** (provided in code)\n\n## Environment Variables\n\nEnvironment variables are automatically parsed with type awareness:\n\n```bash\n# Boolean values\nexport MYAPP_DEBUG=true        # → bool\nexport MYAPP_VERBOSE=1         # → bool (true)\nexport MYAPP_QUIET=false       # → bool\nexport MYAPP_COLORS=yes        # → bool (true)\n\n# Numbers\nexport MYAPP_PORT=3000         # → integer\nexport MYAPP_TIMEOUT=30.5      # → float\n\n# Arrays (comma-separated)\nexport MYAPP_HOSTS=localhost,api.example.com,cdn.example.com  # → array of strings\n\n# JSON objects/arrays\nexport MYAPP_DATABASE='{\"host\":\"localhost\",\"port\":5432}'     # → object\nexport MYAPP_TAGS='[\"production\",\"web\"]'                      # → array\n\n# Strings (default)\nexport MYAPP_NAME=\"My Application\"                           # → string\n```\n\nEnvironment variable naming:\n\n- Prefix: Uppercase version of config name (or custom `env_prefix`)\n- Nested keys: Separated by underscores\n- Hyphens: Converted to underscores\n\nExamples:\n\n- `database.host` → `MYAPP_DATABASE_HOST`\n- `api-key` → `MYAPP_API_KEY`\n- `cache.ttl-seconds` → `MYAPP_CACHE_TTL_SECONDS`\n\n## Type Safety Features\n\n### ✅ Compile-Time Type Checking\n\n- **No runtime type errors** - all types validated at compile time\n- **IDE autocomplete** - full IntelliSense support for config fields\n- **Default values** - define defaults directly in your struct\n- **Optional fields** - use `?T` for optional configuration\n- **Nested structures** - full support for complex nested configs\n\n### Example with All Features\n\n```zig\nconst Config = struct {\n    // Required fields (no default)\n    app_name: []const u8,\n\n    // Optional fields\n    api_key: ?[]const u8 = null,\n\n    // Fields with defaults\n    port: u16 = 8080,\n    debug: bool = false,\n\n    // Nested structures\n    database: struct {\n        host: []const u8 = \"localhost\",\n        port: u16 = 5432,\n        pool_size: u32 = 10,\n    } = .{},\n\n    // Arrays\n    allowed_hosts: [][]const u8 = \u0026.{},\n\n    // Complex types\n    timeouts: struct {\n        connect_ms: u32 = 5000,\n        read_ms: u32 = 30000,\n    } = .{},\n};\n\nvar config = try zig_config.loadConfig(Config, allocator, .{\n    .name = \"myapp\",\n});\ndefer config.deinit(allocator);\n\n// All fields are type-safe!\nstd.debug.print(\"App: {s}, Port: {d}\\n\", .{\n    config.value.app_name,\n    config.value.port,\n});\n```\n\n## Examples\n\n### Basic Usage (Minimal Example)\n\n```zig\nconst Config = struct {\n    port: u16 = 8080,\n    debug: bool = false,\n};\n\nvar config = try zig_config.loadConfig(Config, allocator, .{\n    .name = \"myapp\",\n});\ndefer config.deinit(allocator);\n\n// Fully typed access!\nconst port: u16 = config.value.port;\n```\n\n### Custom Working Directory\n\n```zig\nconst Config = struct {\n    port: u16 = 8080,\n};\n\nvar config = try zig_config.loadConfig(Config, allocator, .{\n    .name = \"myapp\",\n    .cwd = \"/path/to/project\",\n});\ndefer config.deinit(allocator);\n```\n\n### Custom Environment Prefix\n\n```zig\nconst Config = struct {\n    port: u16 = 8080,\n};\n\nvar config = try zig_config.loadConfig(Config, allocator, .{\n    .name = \"myapp\",\n    .env_prefix = \"CUSTOM\",  // Uses CUSTOM** instead of MYAPP**\n});\ndefer config.deinit(allocator);\n```\n\n### Deep Merging\n\n```zig\nconst std = @import(\"std\");\nconst zig_config = @import(\"zig-config\");\n\npub fn main() !void {\n    var gpa = std.heap.GeneralPurposeAllocator(.{}){};\n    defer _ = gpa.deinit();\n    const allocator = gpa.allocator();\n\n    var target_map = std.json.ObjectMap.init(allocator);\n    defer target_map.deinit();\n    try target_map.put(\"a\", .{ .integer = 1 });\n\n    var source_map = std.json.ObjectMap.init(allocator);\n    defer source_map.deinit();\n    try source_map.put(\"b\", .{ .integer = 2 });\n\n    const merged = try zig_config.deepMerge(\n        allocator,\n        .{ .object = target_map },\n        .{ .object = source_map },\n        .{ .strategy = .smart },  // or .replace, .concat\n    );\n    _ = merged;\n\n    // Result: { \"a\": 1, \"b\": 2 }\n}\n```\n\n### Merge Strategies\n\n#### Replace (default for primitives/arrays)\n\n```zig\n.{ .strategy = .replace }\n// Arrays are completely replaced\n// [1, 2] + [3, 4] = [3, 4]\n```\n\n#### Concat (for arrays)\n\n```zig\n.{ .strategy = .concat }\n// Arrays are concatenated with deduplication\n// [1, 2] + [2, 3] = [1, 2, 3]\n```\n\n#### Smart (for object arrays)\n\n```zig\n.{ .strategy = .smart }\n// Object arrays are merged by key (id, name, key, path, type)\n// [{\"id\": 1, \"name\": \"a\"}] + [{\"id\": 1, \"name\": \"b\"}]\n// = [{\"id\": 1, \"name\": \"b\"}]  // merged by id\n```\n\n## Configuration Result\n\n`ConfigResult(T)` is a generic struct parameterized by your config type:\n\n```zig\npub fn ConfigResult(comptime T: type) type {\n    return struct {\n        value: T,                      // Your typed configuration\n        source: ConfigSource,          // Primary source (.file_local, .file_home, .env_vars, .defaults)\n        sources: []SourceInfo,         // All sources that contributed\n        loaded_at: i64,               // Timestamp\n\n        pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void;\n    };\n}\n```\n\n## File Discovery\n\nZigConfig searches for configuration files in this order:\n\n1. Project root: `./myapp.json`, `./myapp.zig`\n2. Config directory: `./config/myapp.json`, `./config/myapp.zig`\n3. Hidden config: `./.config/myapp.json`, `./.config/myapp.zig`\n4. Home directory: `~/.config/myapp.json`, `~/.config/myapp.zig`\n\nExtension priority: `.json` \u003e `.zig`\n\n## Error Handling\n\nZigConfig provides detailed error types:\n\n```zig\npub const ZigConfigError = error{\n    ConfigFileNotFound,\n    ConfigFileInvalid,\n    ConfigFilePermissionDenied,\n    ConfigFileSyntaxError,\n    ConfigValidationFailed,\n    ConfigSchemaViolation,\n    EnvVarParseError,\n    CircularReferenceDetected,\n    MergeStrategyInvalid,\n    CacheError,\n};\n```\n\nExample error handling:\n\n```zig\nconst AppConfig = struct {\n    port: u16 = 8080,\n};\n\nvar config = zig_config.loadConfig(AppConfig, allocator, .{\n    .name = \"myapp\",\n}) catch |err| switch (err) {\n    error.ConfigFileNotFound =\u003e {\n        std.debug.print(\"No config found, using defaults\\n\", .{});\n        return;\n    },\n    error.ConfigFileSyntaxError =\u003e {\n        std.debug.print(\"Invalid JSON in config file\\n\", .{});\n        return error.InvalidConfig;\n    },\n    else =\u003e return err,\n};\ndefer config.deinit(allocator);\n```\n\n## Testing\n\n```bash\nzig build test\n```\n\nAll 20 tests passing! Note: There are 4 known memory \"leaks\" from Zig's JSON parser's internal arena allocator - these are expected and don't affect runtime behavior.\n\n## API Reference\n\n### Main Functions\n\n#### `loadConfig`\n\n```zig\npub fn loadConfig(\n    comptime T: type,\n    allocator: std.mem.Allocator,\n    options: types.LoadOptions,\n) !types.ConfigResult(T)\n```\n\nLoad configuration with full error handling. Returns a typed result.\n\n#### `tryLoadConfig`\n\n```zig\npub fn tryLoadConfig(\n    comptime T: type,\n    allocator: std.mem.Allocator,\n    options: types.LoadOptions,\n) ?types.ConfigResult(T)\n```\n\nLoad configuration, returning `null` on error.\n\n#### `deepMerge`\n\n```zig\npub fn deepMerge(\n    allocator: std.mem.Allocator,\n    target: std.json.Value,\n    source: std.json.Value,\n    options: types.MergeOptions,\n) !std.json.Value\n```\n\nDeep merge two JSON values.\n\n### Types\n\n```zig\npub const LoadOptions = struct {\n    name: []const u8,\n    defaults: ?std.json.Value = null,\n    cwd: ?[]const u8 = null,\n    validate: bool = true,\n    cache: bool = true,\n    cache_ttl: u64 = 300_000,\n    env_prefix: ?[]const u8 = null,\n    merge_strategy: MergeStrategy = .smart,\n};\n\npub const MergeStrategy = enum {\n    replace,\n    concat,\n    smart,\n};\n\npub const ConfigSource = enum {\n    file_local,\n    file_home,\n    package_json,\n    env_vars,\n    defaults,\n};\n```\n\n## License\n\nMIT\n\n## Contributing\n\nContributions welcome! Please ensure:\n\n- All tests pass (`zig build test`)\n- Code follows Zig style guidelines\n- New features include tests\n\n## Acknowledgments\n\nInspired by [bunfig](https://github.com/stacksjs/bunfig) by the Stacks team.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzig-utils%2Fzig-config","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzig-utils%2Fzig-config","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzig-utils%2Fzig-config/lists"}