Documentation
¶
Overview ¶
Package fuda provides struct-tag-first configuration loading for Go.
It supports loading configuration from YAML/JSON files, environment variables, and external references (files, HTTP endpoints), with built-in defaults and validation.
Basic usage:
type Config struct {
Host string `yaml:"host" default:"localhost" env:"APP_HOST"`
Port int `yaml:"port" default:"8080"`
}
var cfg Config
if err := fuda.LoadFile("config.yaml", &cfg); err != nil {
log.Fatal(err)
}
For more control, use the Builder pattern:
loader, err := fuda.New().
FromFile("config.yaml").
WithEnvPrefix("APP_").
Build()
if err != nil {
log.Fatal(err)
}
if err := loader.Load(&cfg); err != nil {
log.Fatal(err)
}
Index ¶
- Variables
- func LoadBytes(data []byte, target any) error
- func LoadEnv(target any) error
- func LoadEnvWithPrefix(prefix string, target any) error
- func LoadFile(path string, target any) error
- func LoadReader(r io.Reader, target any) error
- func MustLoadBytes(data []byte, target any)
- func MustLoadFile(path string, target any)
- func MustLoadReader(r io.Reader, target any)
- func MustSetDefaults(target any, opts ...Option)
- func ResetDefaultFs()
- func SetDefaultFs(fs afero.Fs)
- func SetDefaults(target any, opts ...Option) error
- func Validate(target any, opts ...Option) error
- type Builder
- func (b *Builder) Apply(fn func(*Builder)) *Builder
- func (b *Builder) Build() (*Loader, error)
- func (b *Builder) FromBytes(data []byte) *Builder
- func (b *Builder) FromFile(path string) *Builder
- func (b *Builder) FromReader(r io.Reader) *Builder
- func (b *Builder) WithDotEnv(file string, opts ...DotEnvOption) *Builder
- func (b *Builder) WithDotEnvFiles(files []string, opts ...DotEnvOption) *Builder
- func (b *Builder) WithDotEnvSearch(name string, searchPaths []string, opts ...DotEnvOption) *Builder
- func (b *Builder) WithDurationPreprocess(enabled bool) *Builder
- func (b *Builder) WithEnvPrefix(prefix string) *Builder
- func (b *Builder) WithFilesystem(fs afero.Fs) *Builder
- func (b *Builder) WithOverrides(overrides map[string]any) *Builder
- func (b *Builder) WithRefResolver(r RefResolver) *Builder
- func (b *Builder) WithSizePreprocess(enabled bool) *Builder
- func (b *Builder) WithTemplate(data any, opts ...TemplateOption) *Builder
- func (b *Builder) WithTimeout(timeout time.Duration) *Builder
- func (b *Builder) WithValidator(v *validator.Validate) *Builder
- type ByteSize
- func (b ByteSize) Int() int
- func (b ByteSize) Int64() int64
- func (b ByteSize) MarshalJSON() ([]byte, error)
- func (b ByteSize) MarshalYAML() (any, error)
- func (b ByteSize) String() string
- func (b ByteSize) Uint64() uint64
- func (b *ByteSize) UnmarshalJSON(data []byte) error
- func (b *ByteSize) UnmarshalYAML(node *yaml.Node) error
- type DotEnvOption
- type Duration
- type FieldError
- type LoadError
- type Loader
- type Option
- type RawMessage
- type RefResolver
- type Scanner
- type Setter
- type TemplateOption
- type ValidationError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var DefaultFs afero.Fs = afero.NewOsFs()
DefaultFs is the default filesystem used by fuda for all file operations. It defaults to the OS filesystem but can be overridden for testing.
Example usage for testing:
func TestMyConfig(t *testing.T) {
memFs := afero.NewMemMapFs()
afero.WriteFile(memFs, "/config.yaml", []byte("host: localhost"), 0644)
fuda.SetDefaultFs(memFs)
defer fuda.ResetDefaultFs()
// ... test code ...
}
Functions ¶
func LoadEnv ¶ added in v1.2.0
LoadEnv applies environment variables to target via `env` tags. No file source is read and no defaults are applied. Only env tag processing occurs.
func LoadEnvWithPrefix ¶ added in v1.2.0
LoadEnvWithPrefix is like LoadEnv but prepends prefix to env var names. For example, with prefix "APP_", an `env:"HOST"` tag reads APP_HOST.
func LoadFile ¶
LoadFile parses the file at path and populates target.
Example ¶
ExampleLoadFile demonstrates the simplest usage: loading configuration from a file.
package main
import (
"fmt"
"os"
"github.com/arloliu/fuda"
)
func main() {
// Create a temporary config file for the example
configContent := `
host: example.com
port: 9000
`
if err := os.WriteFile("example_config.yaml", []byte(configContent), 0o600); err != nil {
fmt.Println("failed to write config file")
return
}
defer os.Remove("example_config.yaml")
type Config struct {
Host string `yaml:"host" default:"localhost"`
Port int `yaml:"port" default:"8080"`
}
var cfg Config
if err := fuda.LoadFile("example_config.yaml", &cfg); err != nil {
fmt.Println("failed to load config")
return
}
fmt.Printf("Host: %s, Port: %d\n", cfg.Host, cfg.Port)
}
Output: Host: example.com, Port: 9000
func LoadReader ¶
LoadReader reads from r and populates target.
func MustLoadBytes ¶ added in v1.2.0
MustLoadBytes is like LoadBytes but panics on error. Useful for package-level variable initialization.
func MustLoadFile ¶ added in v1.2.0
MustLoadFile is like LoadFile but panics on error. Useful for package-level variable initialization.
func MustLoadReader ¶ added in v1.2.0
MustLoadReader is like LoadReader but panics on error. Useful for package-level variable initialization.
func MustSetDefaults ¶ added in v1.2.0
MustSetDefaults is like SetDefaults but panics on error. Useful for package-level variable initialization.
func ResetDefaultFs ¶ added in v1.3.0
func ResetDefaultFs()
ResetDefaultFs resets the global filesystem to the OS filesystem. Call this in test cleanup to restore default behavior.
func SetDefaultFs ¶ added in v1.3.0
SetDefaultFs sets the global default filesystem. This is useful for testing scenarios where all file operations should use a memory filesystem or other custom implementation.
WARNING: This modifies global state and is NOT thread-safe. Do not use with t.Parallel() tests. For concurrent tests, use WithFilesystem() on individual builders instead.
Call during test setup (e.g., TestMain), not during test execution.
func SetDefaults ¶ added in v1.2.0
SetDefaults applies `default` tag values to the target struct. Environment variables (via `env` tag) and references (`ref`, `refFrom`) are also resolved, but no YAML/JSON source is processed.
By default, NO validation is performed. Use WithValidation(true) to enable validation.
Example:
type Config struct {
Host string `default:"localhost" validate:"required"`
Timeout int `default:"30"`
}
var cfg Config
// Sets defaults only
if err := fuda.SetDefaults(&cfg); err != nil {
log.Fatal(err)
}
// Sets defaults AND validates
if err := fuda.SetDefaults(&cfg, fuda.WithValidation(true)); err != nil {
log.Fatal(err)
}
Types ¶
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder provides a fluent API for constructing a Loader.
func New ¶
func New() *Builder
New creates a new configuration Builder.
Example ¶
ExampleNew demonstrates the builder pattern for advanced configuration.
package main
import (
"fmt"
"log"
"github.com/arloliu/fuda"
)
func main() {
configContent := `
database:
host: db.example.com
`
type DatabaseConfig struct {
Host string `yaml:"host" default:"localhost"`
Port int `yaml:"port" default:"5432"`
}
type Config struct {
Database DatabaseConfig `yaml:"database"`
}
loader, err := fuda.New().
FromBytes([]byte(configContent)).
WithEnvPrefix("APP_").
Build()
if err != nil {
log.Fatal(err)
}
var cfg Config
if err := loader.Load(&cfg); err != nil {
log.Fatal(err)
}
fmt.Printf("Database: %s:%d\n", cfg.Database.Host, cfg.Database.Port)
}
Output: Database: db.example.com:5432
func (*Builder) Apply ¶
Apply applies a configuration function to the builder. This enables reusable configuration bundles:
var prodConfig = func(b *fuda.Builder) {
b.WithEnvPrefix("PROD_").WithTimeout(10 * time.Second)
}
loader, _ := fuda.New().FromFile("config.yaml").Apply(prodConfig).Build()
func (*Builder) Build ¶
Build creates the Loader with the configured options. Returns an error if any prior builder method (FromFile, FromReader) failed.
func (*Builder) FromBytes ¶
FromBytes uses the provided byte slice as configuration data. The content format (YAML or JSON) is auto-detected.
func (*Builder) FromFile ¶
FromFile reads configuration from the file at path. The file format (YAML or JSON) is auto-detected from content.
func (*Builder) FromReader ¶
FromReader reads configuration from an io.Reader. The content format (YAML or JSON) is auto-detected.
func (*Builder) WithDotEnv ¶ added in v1.2.0
func (b *Builder) WithDotEnv(file string, opts ...DotEnvOption) *Builder
WithDotEnv loads environment variables from a dotenv file before processing. The file is loaded before any `env` tag resolution, so dotenv values become available to struct fields with env tags.
By default, existing environment variables take precedence over dotenv values. Use DotEnvOverride() option to reverse this behavior.
Missing files are silently ignored, making this safe for optional .env.local files.
Example:
loader, _ := fuda.New().
FromFile("config.yaml").
WithDotEnv(".env").
Build()
// With override mode:
loader, _ := fuda.New().
FromFile("config.yaml").
WithDotEnv(".env", fuda.DotEnvOverride()).
Build()
func (*Builder) WithDotEnvFiles ¶ added in v1.2.0
func (b *Builder) WithDotEnvFiles(files []string, opts ...DotEnvOption) *Builder
WithDotEnvFiles loads environment variables from multiple dotenv files. Files are loaded in order; later files can supplement earlier ones. This enables environment-specific overlays:
loader, _ := fuda.New().
FromFile("config.yaml").
WithDotEnvFiles([]string{".env", ".env.local", ".env.production"}).
Build()
Missing files are silently ignored. Use DotEnvOverride() option to override existing env vars.
func (*Builder) WithDotEnvSearch ¶ added in v1.2.0
func (b *Builder) WithDotEnvSearch(name string, searchPaths []string, opts ...DotEnvOption) *Builder
WithDotEnvSearch searches for a dotenv file in the specified directories. The first existing file found wins. This is useful when the application may be run from different working directories:
loader, _ := fuda.New().
FromFile("config.yaml").
WithDotEnvSearch(".env", []string{".", "./config", "/etc/myapp"}).
Build()
The name parameter is the filename to search for (e.g., ".env"). Use DotEnvOverride() option to override existing env vars.
func (*Builder) WithDurationPreprocess ¶ added in v1.5.0
WithDurationPreprocess enables or disables duration-string preprocessing. Default is enabled for backward compatibility.
func (*Builder) WithEnvPrefix ¶
WithEnvPrefix sets a prefix for environment variable lookups. For example, with prefix "APP_", an `env:"HOST"` tag reads APP_HOST.
func (*Builder) WithFilesystem ¶ added in v1.3.0
WithFilesystem sets a custom filesystem for file operations. This is useful for testing with in-memory filesystems.
Example:
memFs := afero.NewMemMapFs()
afero.WriteFile(memFs, "/config.yaml", []byte("host: localhost"), 0644)
loader, _ := fuda.New().
WithFilesystem(memFs).
FromFile("/config.yaml").
Build()
func (*Builder) WithOverrides ¶ added in v1.4.0
WithOverrides sets programmatic overrides that take precedence over config file values. These are applied after template processing but before struct unmarshaling. Keys use dot notation for nested values: "database.host" overrides database.host.
Example:
loader, _ := fuda.New().
FromFile("config.yaml").
WithOverrides(map[string]any{
"host": "override.example.com",
"database.port": 5433,
}).
Build()
func (*Builder) WithRefResolver ¶
func (b *Builder) WithRefResolver(r RefResolver) *Builder
WithRefResolver sets a custom reference resolver for ref/refFrom tags. The default resolver supports file://, http://, and https:// schemes.
func (*Builder) WithSizePreprocess ¶ added in v1.5.0
WithSizePreprocess enables or disables size-string preprocessing. Default is enabled for backward compatibility.
func (*Builder) WithTemplate ¶
func (b *Builder) WithTemplate(data any, opts ...TemplateOption) *Builder
WithTemplate enables Go template processing on configuration content before YAML parsing. The data parameter provides template context, and opts configure template behavior.
Template processing occurs BEFORE YAML unmarshaling. If your configuration contains literal "{{" or "}}" sequences that should not be interpreted as template delimiters, use WithDelimiters to specify alternative delimiters.
Example:
type TemplateData struct {
Host string
Port int
}
loader, _ := fuda.New().
FromFile("config.yaml").
WithTemplate(TemplateData{Host: "localhost", Port: 8080}).
Build()
func (*Builder) WithTimeout ¶
WithTimeout sets a timeout for reference resolution (ref/refFrom tags). Default is 0 (no timeout). Set explicitly for network refs.
type ByteSize ¶ added in v1.5.0
type ByteSize int64
ByteSize represents a size in bytes with human-readable JSON/YAML serialization. It supports parsing both IEC (binary) and SI (decimal) units.
Example:
type Config struct {
MaxFileSize fuda.ByteSize `yaml:"max_file_size"`
}
// YAML: max_file_size: 10MiB
// JSON: {"max_file_size": "10MiB"}
func (ByteSize) MarshalJSON ¶ added in v1.5.0
MarshalJSON outputs size as quoted string.
func (ByteSize) MarshalYAML ¶ added in v1.5.0
MarshalYAML outputs size as string.
func (ByteSize) String ¶ added in v1.5.0
String returns a human-readable representation using IEC units.
func (ByteSize) Uint64 ¶ added in v1.5.0
Uint64 returns the value as uint64. Returns 0 for negative values.
func (*ByteSize) UnmarshalJSON ¶ added in v1.5.0
UnmarshalJSON parses size from string or number (bytes).
type DotEnvOption ¶ added in v1.2.0
type DotEnvOption func(*dotenvConfig)
DotEnvOption configures dotenv loading behavior.
func DotEnvOverride ¶ added in v1.2.0
func DotEnvOverride() DotEnvOption
DotEnvOverride returns an option that causes dotenv values to override existing environment variables. By default, existing env vars take precedence.
type Duration ¶ added in v1.1.0
Duration wraps time.Duration with human-readable JSON/YAML serialization. Unlike time.Duration which marshals to nanoseconds, Duration marshals to a string format (e.g., "1h30m", "5s").
Example:
type Config struct {
Timeout fuda.Duration `yaml:"timeout"`
}
// YAML: timeout: 5s
// JSON: {"timeout": "5s"}
func (Duration) MarshalJSON ¶ added in v1.1.0
MarshalJSON outputs duration as quoted string.
func (Duration) MarshalYAML ¶ added in v1.1.0
MarshalYAML outputs duration as string.
func (*Duration) UnmarshalJSON ¶ added in v1.1.0
UnmarshalJSON parses duration from string or number (nanoseconds).
type FieldError ¶
type FieldError = types.FieldError
FieldError represents an error that occurred while processing a specific field.
type LoadError ¶
LoadError represents an error that occurred during the configuration loading process.
type Loader ¶
type Loader struct {
// contains filtered or unexported fields
}
Loader is responsible for loading configuration from various sources.
func (*Loader) ToKYAML ¶ added in v1.4.0
ToKYAML converts the loader's source to KYAML format. Returns an error if the source is not valid YAML format. KYAML is a strict subset of YAML that is explicit and unambiguous, designed to be halfway between YAML and JSON.
func (*Loader) ToMap ¶ added in v1.4.0
ToMap returns the loader's source configuration as a map. This returns the raw source data (before struct loading, defaults, or env overrides). Useful for debugging, logging, or passing configuration to other systems. Returns an error if no source is set or if YAML parsing fails.
type Option ¶ added in v1.6.0
type Option func(*config)
Option configures Fuda behavior.
func WithValidation ¶ added in v1.6.0
WithValidation enables or disables validation during SetDefaults. By default, SetDefaults does NOT perform validation (pure default setting). Pass WithValidation(true) to opt-in to validation after defaults are applied.
Example:
// Defaults only (no validation) fuda.SetDefaults(&cfg) // Defaults + Validation fuda.SetDefaults(&cfg, fuda.WithValidation(true))
func WithValidator ¶ added in v1.6.0
WithValidator sets a custom validator instance for SetDefaults or Validate. Use this to apply custom validation rules or tags.
For SetDefaults: This option only takes effect if validation is enabled via WithValidation(true).
For Validate: This option overrides the default validator.
Example:
v := validator.New()
v.RegisterValidation("custom", customFunc)
fuda.SetDefaults(&cfg, fuda.WithValidation(true), fuda.WithValidator(v))
type RawMessage ¶ added in v1.1.0
type RawMessage []byte
RawMessage stores raw configuration data for deferred unmarshaling. Use this for polymorphic config where the struct type depends on a discriminator field.
Example:
type DeviceConfig struct {
Type string `yaml:"type"`
Properties fuda.RawMessage `yaml:"properties"`
}
// After loading, unmarshal Properties based on Type:
switch cfg.Type {
case "car":
var props CarProperties
cfg.Properties.Unmarshal(&props)
}
func (RawMessage) MarshalJSON ¶ added in v1.1.0
func (m RawMessage) MarshalJSON() ([]byte, error)
MarshalJSON returns m as-is. User is responsible for ensuring valid JSON.
func (RawMessage) MarshalYAML ¶ added in v1.1.0
func (m RawMessage) MarshalYAML() (any, error)
MarshalYAML implements yaml.Marshaler.
func (RawMessage) Unmarshal ¶ added in v1.1.0
func (m RawMessage) Unmarshal(v any) error
Unmarshal decodes the raw message into v. Uses YAML unmarshaler which handles both YAML and JSON.
func (*RawMessage) UnmarshalJSON ¶ added in v1.1.0
func (m *RawMessage) UnmarshalJSON(data []byte) error
UnmarshalJSON stores a copy of data.
func (*RawMessage) UnmarshalYAML ¶ added in v1.1.0
func (m *RawMessage) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML implements yaml.Unmarshaler.
type RefResolver ¶
type RefResolver interface {
// Resolve returns the content referenced by the uri.
Resolve(ctx context.Context, uri string) ([]byte, error)
}
RefResolver is an interface for resolving references. It is used to mock reference resolution in tests or provide custom resolution logic. Implementations MUST be safe for concurrent use by multiple goroutines.
Example ¶
ExampleRefResolver demonstrates implementing a custom reference resolver.
package main
import (
"context"
"fmt"
)
func main() {
// A simple in-memory resolver for demonstration
type MemoryResolver struct {
secrets map[string]string
}
resolver := &MemoryResolver{
secrets: map[string]string{
"secret://api-key": "my-secret-api-key",
},
}
// Implement the RefResolver interface
resolve := func(_ context.Context, uri string) ([]byte, error) {
if val, ok := resolver.secrets[uri]; ok {
return []byte(val), nil
}
return nil, fmt.Errorf("secret not found: %s", uri)
}
// Use a wrapper type since we can't add methods to MemoryResolver in this example
_ = resolve // In real usage, pass a type implementing RefResolver to WithRefResolver
fmt.Println("Custom RefResolver can handle any URI scheme")
}
Output: Custom RefResolver can handle any URI scheme
type Scanner ¶
Scanner is implemented by custom types to define string-to-value conversion. When processing the default tag, if the target type implements Scanner, its Scan method is called instead of the built-in conversion.
Example:
type LogLevel int
func (l *LogLevel) Scan(src any) error {
s, ok := src.(string)
if !ok {
return fmt.Errorf("expected string, got %T", src)
}
switch strings.ToLower(s) {
case "debug":
*l = 0
case "info":
*l = 1
case "warn":
*l = 2
case "error":
*l = 3
default:
return fmt.Errorf("unknown log level: %s", s)
}
return nil
}
type Setter ¶
Setter is implemented by config structs that need dynamic defaults. SetDefaults is called after all tag processing (default, env, ref) completes.
Example:
type Config struct {
RequestID string
}
func (c *Config) SetDefaults() {
if c.RequestID == "" {
c.RequestID = uuid.New().String()
}
}
type TemplateOption ¶
type TemplateOption func(*templateConfig)
TemplateOption configures template parsing behavior.
func WithDelimiters ¶
func WithDelimiters(left, right string) TemplateOption
WithDelimiters sets custom delimiters for template parsing. Use this when your configuration contains literal "{{" sequences that should not be interpreted as template syntax.
Example:
loader, _ := fuda.New().
FromFile("config.yaml").
WithTemplate(data, fuda.WithDelimiters("<{", "}>")).
Build()
func WithFuncs ¶
func WithFuncs(funcMap template.FuncMap) TemplateOption
WithFuncs adds custom template functions. These are merged with the template's built-in functions.
func WithMissingKey ¶
func WithMissingKey(behavior string) TemplateOption
WithMissingKey controls behavior when a map is indexed with a key not in the map. Valid values:
- "invalid" (default): no error, outputs "<no value>"
- "zero": outputs the zero value for the type
- "error": template execution stops with an error
type ValidationError ¶
type ValidationError = types.ValidationError
ValidationError wraps validation errors from the validator package.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
fuda-doc
module
|
|
|
examples
|
|
|
basic
command
Example: Basic configuration loading with defaults
|
Example: Basic configuration loading with defaults |
|
dotenv
command
Example: Dotenv file loading
|
Example: Dotenv file loading |
|
dsn
command
Example: DSN (Data Source Name) composition
|
Example: DSN (Data Source Name) composition |
|
refs
command
Example: External references with ref and refFrom tags
|
Example: External references with ref and refFrom tags |
|
scanner
command
Example: Custom types with Scanner interface
|
Example: Custom types with Scanner interface |
|
setter
command
Example: Dynamic defaults with Setter interface
|
Example: Dynamic defaults with Setter interface |
|
template
command
Example: Template processing
|
Example: Template processing |
|
validation
command
Example: Validation with go-playground/validator
|
Example: Validation with go-playground/validator |
|
watcher
command
Example: Hot-reload configuration with watcher
|
Example: Hot-reload configuration with watcher |
|
internal
|
|
|
bytesize
Package bytesize provides parsing utilities for human-readable byte size strings.
|
Package bytesize provides parsing utilities for human-readable byte size strings. |
|
vault
module
|
|
|
Package watcher provides hot-reload configuration watching for fuda.
|
Package watcher provides hot-reload configuration watching for fuda. |