config

package module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 10 Imported by: 0

README

gas-config

Test Go Reference Go Version License

Configuration management service for the Gas ecosystem. Supports loading from multiple providers (environment variables, JSON files, .env files) and binding to Go structs.

Features

  • Early initialization: New() returns *Config directly — create and load before the app starts
  • Multiple providers: Environment variables, JSON files, .env files, and custom providers
  • Hierarchical config: Nested access via dot notation (e.g., database.host)
  • Struct binding: Type-safe binding with validation support
  • Sensible defaults: Environment provider is registered automatically

Installation

go get github.com/gasmod/gas-config

Usage

Standalone
package main

import (
    config "github.com/gasmod/gas-config"
    "github.com/gasmod/gas-config/providers"
)

func main() {
    // Create and load config before anything else
    cfg := config.New(
        config.WithProvider(
            providers.NewJSONProvider(
                providers.WithJSONFilePath("config.json"),
            ),
        ),
        config.WithProvider(providers.NewDotEnvProvider()),
    )

    if err := cfg.Load(); err != nil {
        panic(err)
    }

    // Bind to a struct
    var appCfg AppConfig
    if err := cfg.Bind(&appCfg); err != nil {
        panic(err)
    }
}
With Gas App

Config is loaded before app.Run() and registered as a singleton instance so other services can receive it as gas.ConfigProvider via constructor injection.

package main

import (
    "github.com/gasmod/gas"
    config "github.com/gasmod/gas-config"
    "github.com/gasmod/gas-config/providers"
)

func main() {
    cfg := config.New(
        config.WithProvider(providers.NewDotEnvProvider()),
        config.WithProvider(
            providers.NewJSONProvider(
                providers.WithJSONFilePath("config.json"),
            ),
        ),
    )

    if err := cfg.Load(); err != nil {
        panic(err)
    }

    app := gas.NewApp(
        gas.WithServiceInstance[gas.ConfigProvider](cfg),
        // other services ...
    )

    app.Run()
}

type AppConfig struct {
    Database struct {
        Host string
        Port int
    }
    Server struct {
        Host string
        Port int
    }
}

Providers

Environment variables (default)

An EnvProvider is automatically added if none is provided. Environment variables are lowercased and kept as flat snake_case keys by default:

export DATABASE_HOST=localhost   # → database_host
export DATABASE_PORT=5432        # → database_port

To create nested maps, use __ (double underscore) as the separator:

export DATABASE__HOST=localhost  # → database.host (nested)
export DATABASE__PORT=5432       # → database.port (nested)
cfg := config.New() // EnvProvider included by default

Options:

config.New(
    config.WithProvider(
        providers.NewEnvProvider(
            providers.WithEnvPrefix("APP"),
            providers.WithEnvSeparator("__"),
        ),
    ),
)
JSON files
config.New(
    config.WithProvider(
        providers.NewJSONProvider(
            providers.WithJSONFilePath("config.json"),
            providers.WithJSONFileFS(embeddedFS), // optional: custom fs.FS
        ),
    ),
)
.env files

.env variables follow the same key conventions as env vars: lowercased, flat snake_case by default; use __ for nesting.

config.New(
    config.WithProvider(
        providers.NewDotEnvProvider(
            providers.WithDotEnvFilePath(".env"),
            providers.WithDotEnvFileNotFoundPanic(false),
            providers.WithDotEnvFileAppendToOSEnv(true),
        ),
    ),
)
Custom providers

Implement the Provider interface:

type Provider interface {
    Name() string
    Load() (map[string]any, error)
}

Provider ordering

Later providers override earlier ones. The auto-registered EnvProvider is prepended, so explicit providers take precedence:

cfg := config.New(
    config.WithProvider(providers.NewJSONProvider(...)),    // base config
    config.WithProvider(providers.NewDotEnvProvider()),     // overrides JSON
    // EnvProvider is prepended automatically (lowest priority)
)

Reading values

// Get a single value
host := cfg.Get("database.host")

// Check if a value exists
val, exists := cfg.Find("database.host")

// Set defaults (won't override loaded values)
cfg.SetDefault("database.port", 5432)

// Set a value (overrides loaded values)
cfg.Set("database.host", "127.0.0.1")

// Get all values
allValues := cfg.Values()

Struct binding

Bind() maps configuration into structs using reflection. Field matching uses json tags first, then case-insensitive field names:

type DBConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Password string `json:"password" validate:"required"`
}

var dbCfg DBConfig
if err := cfg.Bind(&dbCfg); err != nil {
    log.Fatal(err)
}

// Disable validation
cfg.Bind(&dbCfg, config.WithValidate(false))

Supported types: all int/uint/float variants, bool, string, time.Duration, slices (including comma-separated strings), maps, nested structs, and embedded structs.

Custom validator

Pass config.WithValidator to New to use a caller-owned *validator.Validate (e.g. with custom tags registered). If omitted, a new validator.New() instance is used internally.

v := validator.New()
v.RegisterValidation("mytag", myValidatorFunc)

cfg := config.New(config.WithValidator(v))

Extensions

Extensions provide pre/post-load hooks:

type Extension interface {
    Name() string
    PreLoad(ctx context.Context, cfg *Config) error
    PostLoad(ctx context.Context, cfg *Config) error
}
cfg := config.New(config.WithExtension(myExtension))
gas-env

The gas-env extension (extensions/gas-env) manages application environment detection and validation. It resolves the current environment from config providers, the GAS_ENV OS variable, or a default, and makes it available throughout the app.

import (
    config "github.com/gasmod/gas-config"
    gasenv "github.com/gasmod/gas-config/extensions/gas-env"
)

envExt := gasenv.NewExtension()

cfg := config.New(
    config.WithProvider(providers.NewDotEnvProvider()),
    config.WithExtension(envExt),
)

if err := cfg.Load(); err != nil {
    panic(err)
}

fmt.Println(envExt.Current())       // "development"
fmt.Println(envExt.IsProduction())  // false
fmt.Println(envExt.IsDevelopmentLike()) // true

Environments: Development, Testing, Staging, Production

Resolution priority:

  1. Config providers (JSON, .env, etc.)
  2. OS environment variable (GAS_ENV by default)
  3. Default (Development)

Options:

gasenv.NewExtension(
    gasenv.WithEnvVarName("APP_ENV"),
    gasenv.WithDefault(gasenv.Production),
    gasenv.WithConfigKey("AppEnv"),
    gasenv.WithAllowedEnvs(gasenv.Production, gasenv.Staging),
)

Embedding in config structs:

type AppConfig struct {
    gasenv.WithGasEnv // adds GasEnv field, auto-populated by Bind()
    Database struct {
        Host string
        Port int
    }
}

var appCfg AppConfig
cfg.Bind(&appCfg)
fmt.Println(appCfg.GasEnv.IsProduction()) // false

Testing

The configtest package provides a mock that structurally satisfies gas.ConfigProvider:

import "github.com/gasmod/gas-config/configtest"

mock := &configtest.MockConfig{}
mock.GetFn = func(key string) any {
	if key == "database.host" {
		return "localhost"
	}
	return nil
}

// assert calls:
if mock.CallCount("Get") != 1 {
	t.Error("expected one Get call")
}

For tests that need real Get/Find/Bind semantics seeded with known values, use NewMockConfigWithValues, which delegates to a real *config.Config under the hood and avoids leaking real environment variables:

mock, err := configtest.NewMockConfigWithValues(map[string]any{
	"database.host": "localhost",
	"database.port": 5432,
})

Individual Fn fields can still be overridden afterwards, and Calls/Reset/CallCount work as usual.

Examples

See examples/ for complete examples:

  • basic/ - Environment variables
  • env/ - Environment variables with prefix
  • json/ - JSON configuration
  • json_fs/ - JSON with embedded filesystem
  • dotenv/ - .env files

Documentation

Overview

Package config provides a flexible configuration management system that supports reading from multiple providers and binding to user-defined types.

Package config provides configuration management: env, JSON, and .env providers; struct binding; hierarchical dot-notation access; extensions with pre/post-load hooks.

See the module README for usage examples and design rationale.

SPDX-License-Identifier: MIT

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrProviderLoadFailed indicates failure to load configuration from a provider.
	ErrProviderLoadFailed = errors.New("failed to load from provider")

	// ErrExtensionPreLoadHookFailed indicates a failure while executing the pre-load hook of an extension.
	ErrExtensionPreLoadHookFailed = errors.New("failed to execute extension pre-load hook")

	// ErrExtensionPostLoadHookFailed indicates a failure when executing the post-load hook of an extension.
	ErrExtensionPostLoadHookFailed = errors.New("failed to execute extension post-load hook")

	// ErrNilValues is returned when a nil value is provided where non-nil input is required.
	ErrNilValues = errors.New("values cannot be nil")
)

Functions

This section is empty.

Types

type BindOption

type BindOption func(*BindOptions)

BindOption is a functional option for configuring Bind behavior by modifying BindOptions.

func WithValidate

func WithValidate(validate bool) BindOption

WithValidate sets the validation flag in the BindOptions.

type BindOptions

type BindOptions struct {
	// contains filtered or unexported fields
}

BindOptions defines options for binding configuration data to a struct.

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config represents the configuration loaded from various providers.

func New

func New(opts ...Option) *Config

New creates and returns a new Config instance, applying the provided functional options. If no providers are specified, an EnvProvider is added as the default.

func (*Config) Bind

func (c *Config) Bind(dest any, options ...BindOption) error

Bind binds the configuration to the provided struct.

func (*Config) Find

func (c *Config) Find(key string) (value any, exist bool)

Find searches for and retrieves a configuration value by key. Supports hierarchical paths like "database.host".

func (*Config) Get

func (c *Config) Get(key string) any

Get retrieves a configuration value by key. Supports hierarchical paths like "database.host".

func (*Config) Load

func (c *Config) Load() error

Load loads configuration from all registered providers and applies pre/post-Load hooks defined by extensions.

Returns an error if any provider or extension hook fails during the loading process.

func (*Config) LoadWithContext

func (c *Config) LoadWithContext(ctx context.Context) error

LoadWithContext loads configuration with the provided context, executing pre-Load and post-Load hooks for extensions.

func (*Config) Set

func (c *Config) Set(key string, value any)

Set sets a value for the specified key in the configuration, overriding any existing value. It creates nested maps if they do not exist.

func (*Config) SetDefault

func (c *Config) SetDefault(key string, value any)

SetDefault sets a default value for the specified key in the configuration. It creates nested maps if they do not exist, but does not override existing values.

func (*Config) SetDefaults

func (c *Config) SetDefaults(values any) error

SetDefaults sets default configuration values from a struct or map without overriding existing values. Returns an error if the input is invalid or nil.

func (*Config) Values

func (c *Config) Values() map[string]any

Values returns the configuration values.

type Extension

type Extension interface {
	Name() string
	PreLoad(ctx context.Context, cfg *Config) error
	PostLoad(ctx context.Context, cfg *Config) error
}

Extension defines an interface for executing actions during the configuration loading process. The Name method is used to identify the extension by name. The PreLoad method is invoked prior to the main configuration loading phase. The PostLoad method is invoked after the main configuration loading phase.

type Option

type Option func(*Config)

Option configures the config service constructor.

func WithExtension

func WithExtension(ext Extension) Option

WithExtension registers an extension that provides pre/post-Load hooks.

func WithProvider

func WithProvider(p providers.Provider) Option

WithProvider adds a configuration provider (env, JSON, .env, etc.).

func WithValidator

func WithValidator(v *validator.Validate) Option

WithValidator sets a custom validator instance, allowing callers to register custom validation tags before constructing the Config. If not provided, a new validator.New() instance is used.

Directories

Path Synopsis
Package configtest provides a mock implementation of the config provider API for use in tests.
Package configtest provides a mock implementation of the config provider API for use in tests.
examples
env module
extensions
gas-env
Package gasenv provides an extension for the config configuration library that manages application environments (development, testing, staging, production).
Package gasenv provides an extension for the config configuration library that manages application environments (development, testing, staging, production).
internal
dotenv
Package dotenv provides functionality for parsing dotenv-style configuration files.
Package dotenv provides functionality for parsing dotenv-style configuration files.
env
Package env provides utilities for parsing environment variables into nested Go data structures.
Package env provides utilities for parsing environment variables into nested Go data structures.
maputils
Package maputils provides utilities for deep binding and merging of maps into Go data structures.
Package maputils provides utilities for deep binding and merging of maps into Go data structures.
providers
Package providers implements a base FS-based configuration provider.
Package providers implements a base FS-based configuration provider.
reflection
Package reflection provides utilities for working with Go's reflection system.
Package reflection provides utilities for working with Go's reflection system.
sysfs
Package sysfs provides a file system implementation that ensures safe file operations.
Package sysfs provides a file system implementation that ensures safe file operations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL