fuda

package module
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Feb 1, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

README

Fuda (札)

Fuda Logo

Go Reference

⛩️ Spiritual protection and hydration for your Go configurations.

Fuda is a lightweight, struct-tag-first configuration library for Go — with built-in defaults, environment overrides, secret references, and validation.

Why Fuda?

The name comes from 御札 (ofuda) — traditional Japanese talismans inscribed with sacred characters to ward off evil and bring protection. Just as an ofuda guards a home, fuda guards your application:

  • 📜 Inscribe your struct fields with tags like default, env, and ref
  • 🔮 Summon configuration from files, environment, or remote URLs
  • 🛡️ Protect integrity with validation rules
  • 🔐 Guard secrets by resolving them securely at runtime (Docker secrets, vaults)

No more scattered os.Getenv() calls. No more manual YAML wrangling. Just declare your config struct, add the sacred tags, and let fuda perform the ritual.

Installation

go get github.com/arloliu/fuda

Quick Start

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/arloliu/fuda"
)

type Config struct {
    Host    string        `yaml:"host" default:"localhost" env:"APP_HOST"`
    Port    int           `yaml:"port" default:"8080" env:"APP_PORT"`
    Timeout time.Duration `yaml:"timeout" default:"30s"`
    Debug   bool          `yaml:"debug" default:"false"`
}

func main() {
    var cfg Config

    // Load from file
    if err := fuda.LoadFile("config.yaml", &cfg); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Server: %s:%d\n", cfg.Host, cfg.Port)
}

Features

  • YAML/JSON parsing with struct tag support
  • Default values via default tag
  • Environment overrides via env tag with optional prefix
  • Dotenv file loading via WithDotEnv() with overlay and override support
  • External references via ref and refFrom tags (file://, http://, https://, vault://)
  • DSN composition via dsn tag for building connection strings from fields
  • HashiCorp Vault integration via fuda/vault package (Token, Kubernetes, AppRole auth)
  • Hot-reload configuration via fuda/watcher package with fsnotify
  • Template processing via Go's text/template for dynamic configuration
  • Testable filesystem via afero abstraction for easy testing with in-memory filesystems
  • Custom type conversion via Scanner interface
  • Dynamic defaults via Setter interface
  • Duration type (fuda.Duration) with human-friendly parsing (e.g., "30s", "5m", "1h30m", "7d")
  • ByteSize type (fuda.ByteSize) for human-readable byte sizes (e.g., "64KiB", "10MiB", "2GB")
  • Byte size parsing for integer fields (e.g., "64KiB", "10MiB", "2GB")
  • Preprocessing toggles for duration/size strings via builder options
  • RawMessage type for deferred/polymorphic JSON/YAML unmarshaling
  • Validation using go-playground/validator

Documentation

API

Builder Pattern
loader, err := fuda.New().
    FromFile("config.yaml").           // or FromBytes(), FromReader()
    WithEnvPrefix("APP_").             // optional: prefix for env vars
    WithFilesystem(memFs).             // optional: custom filesystem (afero)
    WithDotEnv(".env").                // optional: load .env file
    WithTimeout(10 * time.Second).     // optional: timeout for ref resolution
    WithValidator(customValidator).    // optional: custom validator
    WithRefResolver(customResolver).   // optional: custom ref resolver
    WithTemplate(templateData).        // optional: template processing
    WithDurationPreprocess(true).      // optional: enable/disable duration preprocessing
    WithSizePreprocess(true).          // optional: enable/disable size preprocessing
    Build()

if err != nil {
    log.Fatal(err)
}

var cfg Config
if err := loader.Load(&cfg); err != nil {
    log.Fatal(err)
}
Template Processing

Process configuration as a Go template before parsing:

type TemplateData struct {
    Env  string
    Host string
}

data := TemplateData{Env: "prod", Host: "api.example.com"}

loader, _ := fuda.New().
    FromFile("config.yaml").
    WithTemplate(data).
    Build()

With config.yaml:

host: "{{ .Host }}"
Custom delimiters can be set if your config contains literal `{{` sequences:

```go
WithTemplate(data, fuda.WithDelimiters("<{", "}>"))
Dotenv Loading

Load environment variables from .env files before processing:

// Single file
loader, _ := fuda.New().
    FromFile("config.yaml").
    WithDotEnv(".env").
    Build()

// Multiple files (overlay pattern)
loader, _ := fuda.New().
    FromFile("config.yaml").
    WithDotEnvFiles([]string{".env", ".env.local", ".env.production"}).
    Build()

// Override mode (dotenv values override existing env vars)
loader, _ := fuda.New().
    FromFile("config.yaml").
    WithDotEnv(".env", fuda.DotEnvOverride()).
    Build()

Missing files are silently ignored, making this safe for optional overlays like .env.local.

DSN Composition

Build connection strings from config fields and secrets using the dsn tag:

type Config struct {
    DBHost     string `yaml:"host" default:"localhost"`
    DBUser     string `env:"DB_USER"`
    DBPassword string `ref:"vault:///secret/data/db#password"`

    // Compose DSN from fields above, or inline secrets/env vars
    DSN string `dsn:"postgres://${.DBUser}:${.DBPassword}@${.DBHost}:5432/app"`
}

Inline secret and environment variable resolution:

// Inline vault secret
DSN string `dsn:"postgres://${ref:vault:///db#user}:${ref:vault:///db#pass}@host:5432/db"`

// Inline environment variables
DSN string `dsn:"redis://${env:REDIS_HOST}:${env:REDIS_PORT}/0"`
Convenience Functions
// Load from file
fuda.LoadFile("config.yaml", &cfg)

// Load from bytes
fuda.LoadBytes(yamlData, &cfg)

// Load from reader
fuda.LoadReader(reader, &cfg)

Important Notes

Topic Details
Timeout Default is 0 (no timeout). Set explicitly with WithTimeout() for network refs.
Blocking I/O Load() blocks during file/network operations. Run in a goroutine if needed.
File URIs Supports file:///absolute/path and file://relative/path formats.

Thread Safety

Component Thread-Safe?
Loader ✅ Yes — safe to call Load() from multiple goroutines after Build()
RefResolver ⚠️ Implementations must be thread-safe if Loader is shared

A Loader instance does not mutate state after construction and can be safely reused.

Setter Interface

Implement Setter for dynamic defaults that can't be expressed as static tag values:

type Config struct {
    RequestID string
    CreatedAt time.Time
}

func (c *Config) SetDefaults() {
    if c.RequestID == "" {
        c.RequestID = uuid.New().String()
    }
    if c.CreatedAt.IsZero() {
        c.CreatedAt = time.Now()
    }
}
Scanner Interface

Implement Scanner for custom string-to-value conversion in default tags:

type LogLevel int

const (Debug LogLevel = iota; Info; Warn; Error)

func (l *LogLevel) Scan(src any) error {
    s, _ := src.(string)
    switch strings.ToLower(s) {
    case "debug": *l = Debug
    case "info":  *l = Info
    case "warn":  *l = Warn
    case "error": *l = Error
    default: return fmt.Errorf("unknown level: %s", s)
    }
    return nil
}

// Usage
type Config struct {
    Level LogLevel `default:"info"`
}

Error Handling

The library provides typed errors for inspection:

Error Type When Returned
*FieldError Invalid tag value, type conversion failure, or ref resolution failure
*LoadError Multiple field errors during a single load operation
*ValidationError Validation rules from validate tag failed

All errors support errors.Is() and errors.Unwrap() for error chain inspection.

var fieldErr *fuda.FieldError
if errors.As(err, &fieldErr) {
    fmt.Printf("Field: %s, Tag: %s\n", fieldErr.Path, fieldErr.Tag)
}

var validationErr *fuda.ValidationError
if errors.As(err, &validationErr) {
    // Handle validation failures
}

Examples

Working examples are available in the examples/ directory:

Example Description
basic Loading config with defaults, env overrides, and validation
dotenv Loading environment variables from .env files
dsn Composing connection strings using dsn tag
refs External references with ref and refFrom tags
scanner Custom type conversion via Scanner interface
setter Dynamic defaults via Setter interface
template Go template processing for dynamic config
validation Struct validation with validate tag
watcher Hot-reload configuration with fsnotify

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

Examples

Constants

This section is empty.

Variables

View Source
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 LoadBytes

func LoadBytes(data []byte, target any) error

LoadBytes parses the raw bytes and populates target.

func LoadEnv added in v1.2.0

func LoadEnv(target any) error

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

func LoadEnvWithPrefix(prefix string, target any) error

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

func LoadFile(path string, target any) error

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

func LoadReader(r io.Reader, target any) error

LoadReader reads from r and populates target.

func MustLoadBytes added in v1.2.0

func MustLoadBytes(data []byte, target any)

MustLoadBytes is like LoadBytes but panics on error. Useful for package-level variable initialization.

func MustLoadFile added in v1.2.0

func MustLoadFile(path string, target any)

MustLoadFile is like LoadFile but panics on error. Useful for package-level variable initialization.

func MustLoadReader added in v1.2.0

func MustLoadReader(r io.Reader, target any)

MustLoadReader is like LoadReader but panics on error. Useful for package-level variable initialization.

func MustSetDefaults added in v1.2.0

func MustSetDefaults(target any, opts ...Option)

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

func SetDefaultFs(fs afero.Fs)

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

func SetDefaults(target any, opts ...Option) error

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)
}

func Validate added in v1.2.0

func Validate(target any, opts ...Option) error

Validate runs validation on target using the `validate` tag. No loading, default processing, or env resolution occurs. Only validation is performed.

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

func (b *Builder) Apply(fn func(*Builder)) *Builder

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

func (b *Builder) Build() (*Loader, error)

Build creates the Loader with the configured options. Returns an error if any prior builder method (FromFile, FromReader) failed.

func (*Builder) FromBytes

func (b *Builder) FromBytes(data []byte) *Builder

FromBytes uses the provided byte slice as configuration data. The content format (YAML or JSON) is auto-detected.

func (*Builder) FromFile

func (b *Builder) FromFile(path string) *Builder

FromFile reads configuration from the file at path. The file format (YAML or JSON) is auto-detected from content.

func (*Builder) FromReader

func (b *Builder) FromReader(r io.Reader) *Builder

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

func (b *Builder) WithDurationPreprocess(enabled bool) *Builder

WithDurationPreprocess enables or disables duration-string preprocessing. Default is enabled for backward compatibility.

func (*Builder) WithEnvPrefix

func (b *Builder) WithEnvPrefix(prefix string) *Builder

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

func (b *Builder) WithFilesystem(fs afero.Fs) *Builder

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

func (b *Builder) WithOverrides(overrides map[string]any) *Builder

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

func (b *Builder) WithSizePreprocess(enabled bool) *Builder

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

func (b *Builder) WithTimeout(timeout time.Duration) *Builder

WithTimeout sets a timeout for reference resolution (ref/refFrom tags). Default is 0 (no timeout). Set explicitly for network refs.

func (*Builder) WithValidator

func (b *Builder) WithValidator(v *validator.Validate) *Builder

WithValidator sets a custom validator instance. If not set, a default validator is used.

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) Int added in v1.5.0

func (b ByteSize) Int() int

Int returns the value as int.

func (ByteSize) Int64 added in v1.5.0

func (b ByteSize) Int64() int64

Int64 returns the underlying int64 value (bytes).

func (ByteSize) MarshalJSON added in v1.5.0

func (b ByteSize) MarshalJSON() ([]byte, error)

MarshalJSON outputs size as quoted string.

func (ByteSize) MarshalYAML added in v1.5.0

func (b ByteSize) MarshalYAML() (any, error)

MarshalYAML outputs size as string.

func (ByteSize) String added in v1.5.0

func (b ByteSize) String() string

String returns a human-readable representation using IEC units.

func (ByteSize) Uint64 added in v1.5.0

func (b ByteSize) Uint64() uint64

Uint64 returns the value as uint64. Returns 0 for negative values.

func (*ByteSize) UnmarshalJSON added in v1.5.0

func (b *ByteSize) UnmarshalJSON(data []byte) error

UnmarshalJSON parses size from string or number (bytes).

func (*ByteSize) UnmarshalYAML added in v1.5.0

func (b *ByteSize) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML 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

type Duration time.Duration

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) Duration added in v1.1.0

func (d Duration) Duration() time.Duration

Duration returns the underlying time.Duration value.

func (Duration) MarshalJSON added in v1.1.0

func (d Duration) MarshalJSON() ([]byte, error)

MarshalJSON outputs duration as quoted string.

func (Duration) MarshalYAML added in v1.1.0

func (d Duration) MarshalYAML() (any, error)

MarshalYAML outputs duration as string.

func (Duration) String added in v1.1.0

func (d Duration) String() string

String returns the duration string (e.g., "1h30m5s").

func (*Duration) UnmarshalJSON added in v1.1.0

func (d *Duration) UnmarshalJSON(data []byte) error

UnmarshalJSON parses duration from string or number (nanoseconds).

func (*Duration) UnmarshalYAML added in v1.1.0

func (d *Duration) UnmarshalYAML(node *yaml.Node) error

UnmarshalYAML 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

type LoadError = types.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) Load

func (l *Loader) Load(target any) error

Load populates the target struct with configuration.

func (*Loader) ToKYAML added in v1.4.0

func (l *Loader) ToKYAML() ([]byte, error)

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

func (l *Loader) ToMap() (map[string]any, error)

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

func WithValidation(enabled bool) Option

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

func WithValidator(v *validator.Validate) Option

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

type Scanner = types.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

type Setter = types.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.

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.

Jump to

Keyboard shortcuts

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