Documentation
¶
Overview ¶
Package confx unifies command-line flags, environment variables, configuration files (YAML/JSON/TOML), and struct-defined defaults into a single typed loader.
Recommended: embed a default config file as living documentation ¶
The recommended way to supply defaults is to keep them in a YAML file that you embed into the binary and load via Read, rather than hand-writing a default struct literal:
//go:embed default-config.yaml
var defaultConfigYAML string
def, err := confx.Read[*Config]("yaml", strings.NewReader(defaultConfigYAML))
if err != nil {
return err
}
loader, err := confx.Initialize(def)
A committed, embedded default config file doubles as living documentation of the command's configuration:
- It lists every configuration option the command supports.
- It shows the default value of each option.
- Comments in the file describe what each option does.
- Users can copy it verbatim, then trim or override only what they need.
See examples/config for a complete, working example of this pattern.
Index ¶
- Variables
- func Read[T any](typ string, r io.Reader) (T, error)
- func ReadWithTagName[T any](tagName string, typ string, r io.Reader) (T, error)
- func StringToMapHookFunc(separator string, pairSeparator string) mapstructure.DecodeHookFunc
- func StringToSliceHookFunc(separator string) mapstructure.DecodeHookFunc
- type ExpectedValidation
- type ExpectedValidationError
- type Field
- type Loader
- type Option
- func WithEnvPrefix(envPrefix string) Option
- func WithFieldHook(hook func(f *Field) (*Field, error)) Option
- func WithFlagSet(flagSet *pflag.FlagSet) Option
- func WithTagName(tagName string) Option
- func WithUsageTagName(usageTagName string) Option
- func WithValidator(v Validator) Option
- func WithViper(v *viper.Viper) Option
- type ValidationSuite
- type Validator
- type ValidatorFunc
Constants ¶
This section is empty.
Variables ¶
var DecoderConfigOption = func(tagName string) func(dc *mapstructure.DecoderConfig) { return func(dc *mapstructure.DecoderConfig) { dc.DecodeHook = mapstructure.ComposeDecodeHookFunc( mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeHookFunc(time.RFC3339), StringToSliceHookFunc(","), StringToMapHookFunc(",", "="), ) if tagName != "" { dc.TagName = tagName } else { dc.TagName = DefaultTagName } } }
var DefaultTagName = "confx"
DefaultTagName is the default tag name for struct fields
var DefaultUsageTagName = "usage"
DefaultUsageTagName is the default tag name for field usage descriptions
Functions ¶
func ReadWithTagName ¶
func StringToMapHookFunc ¶
func StringToMapHookFunc(separator string, pairSeparator string) mapstructure.DecodeHookFunc
func StringToSliceHookFunc ¶
func StringToSliceHookFunc(separator string) mapstructure.DecodeHookFunc
Types ¶
type ExpectedValidation ¶
type ExpectedValidation struct {
Config any // The config instance to validate
Name string
ExpectedErrors []ExpectedValidationError // Expected validation errors. if empty, no validation errors are expected.
}
ExpectedValidation represents the expected validation result for a config.
type ExpectedValidationError ¶
type ExpectedValidationError struct {
Path string // Path to the field that should fail validation, using dot notation for nested fields
Tag string // Expected validation tag that should fail
}
ExpectedValidationError represents an expected validation error for testing purposes.
type Loader ¶
func Initialize ¶
Initialize sets up configuration binding by automatically registering command-line flags, binding environment variables, loading configuration files, and validating the final configuration.
It leverages reflection to traverse the fields of the provided default configuration struct, supports various data types including basic types, slices, maps, and nested structs defined separately, and integrates with Viper for configuration management and go-playground/validator for validation.
Note: Even if a field in the default configuration is a nil pointer, it will be assigned a zero value after loading. This is because flags typically require a default value, which is usually not nil. This behavior aligns with standard configuration requirements, ensuring that all fields are initialized with usable values rather than nil pointers.
Parameters:
- def: The default configuration struct.
Returns:
- Loader[T]: A generic loader function that accepts an optional configuration file path. When invoked, it parses the command-line flags, binds them along with environment variables, loads the configuration file if provided, unmarshal the configuration into the struct, and validates it.
- error: An error object if initialization fails.
type Option ¶
type Option func(opts *initOptions)
func WithEnvPrefix ¶
WithEnvPrefix sets a custom environment variable prefix for reading configuration from environment variables. If not set, environment variables are read without a prefix.
func WithFieldHook ¶
WithFieldHook sets a custom field hook function that maps configuration field names to Viper keys, flag names, environment variable names and usage strings.
func WithFlagSet ¶
WithFlagSet sets a custom pflag.FlagSet instance for parsing command line flags If not set, the default pflag.CommandLine is used.
func WithTagName ¶
WithTagName sets a custom struct tag name for reading configuration from struct fields. If not set, the default tag name is "confx".
func WithUsageTagName ¶
WithUsageTagName sets a custom struct tag name for reading field usage descriptions. If not set, the default tag name is "usage".
func WithValidator ¶
WithValidator sets a custom validator instance for validating the configuration struct. If not set, the default ValidatorWithSkipNestedUnless(validator.New(validator.WithRequiredStructEnabled())) is used.
type ValidationSuite ¶
type ValidationSuite struct {
// contains filtered or unexported fields
}
ValidationSuite provides utilities for testing config validation rules.
func NewValidationSuite ¶
func NewValidationSuite(t *testing.T) *ValidationSuite
NewValidationSuite creates a new ValidationSuite for testing config validation rules.
Example:
type MyConfig struct {
Name string `validate:"required"`
Age int `validate:"gte=0,lte=150"`
}
func TestMyConfigValidation(t *testing.T) {
suite := confx.NewValidationSuite(t)
suite.RunTests([]confx.ValidationExpectation{
{
Name: "valid config",
Config: &MyConfig{Name: "John", Age: 30},
},
{
Name: "invalid config",
Config: &MyConfig{Age: -1},
ExpectedErrors: []confx.ValidationError{
{Path: "Name", Tag: "required"},
{Path: "Age", Tag: "gte"},
},
},
})
}
func (*ValidationSuite) RunTests ¶
func (s *ValidationSuite) RunTests(expectations []ExpectedValidation)
RunTests runs a set of validation tests.
It takes a slice of ExpectedValidation and runs each test case in a separate subtest. For each test case, it validates the given config using the embedded validator and checks that the validation errors match the expected errors. If the expected errors are empty, it checks that the config is valid.
It also checks for duplicate expectations and unexpected errors.
func (*ValidationSuite) WithCustomValidator ¶
func (s *ValidationSuite) WithCustomValidator(v Validator) *ValidationSuite
WithCustomValidator returns a new ValidationSuite that uses the provided validator. This is useful when you need to test custom validation rules.
type Validator ¶
type Validator interface {
RegisterValidationCtx(tag string, fn validator.FuncCtx, callValidationEvenIfNull ...bool) error
StructCtx(ctx context.Context, v any) error
}
func ValidatorWithSkipNestedUnless ¶
ValidatorWithSkipNestedUnless wraps a validator with support for conditional nested struct validation using the "skip_nested_unless" tag. This allows you to skip validation of nested structs based on the values of other fields in the parent struct.
The wrapper performs two main functions:
- Registers the "skip_nested_unless" validation tag
- Filters out validation errors from skipped nested structs
Parameters:
- validator: The base validator to wrap with skip_nested_unless support
Returns:
- Validator: A wrapped validator that supports the skip_nested_unless tag
Panics if registration of the skip_nested_unless validation fails