Skip to content

Latest commit

History

59 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Fuda

Fuda logo

CIGo ReferenceGo Report CardLicense

Struct tags in, a validated config out. Fuda reads your YAML or JSON file, layers in defaults and environment overrides, resolves secrets and connection strings, and validates the result. All of it is declared on the struct itself, with no glue code in main().

Read the documentation site for the complete beginner-first guide, or the package documentation for the API reference.

Why Fuda

  • One struct, one source of truth. Keys, defaults, env vars, secrets, and validation rules live next to the field they describe. Nothing is spread across a config struct, a flags package, and a validator call.
  • Secrets without a secrets SDK. Pull values from files, HTTP endpoints, or Vault straight into a field with ref/refFrom. Compose connection strings from those values with dsn, with no manual string building.
  • Human input, typed output. Write 10MiB or 7d in config and get an int64 or time.Duration back. Write debug and get your own enum type back via the Scanner interface.
  • Fails loudly, not late.validate tags check the fully loaded struct before your program starts. You get field-level errors instead of a nil pointer three hours into a shift.
  • Grows with you. Start with fuda.LoadFile, then reach for the builder for env prefixes, dotenv overlays, templated config, or live file/remote watching. None of that requires changing the struct you already wrote.

Install

go get github.com/arloliu/fuda

Quick example

One Load call resolves a secret, composes a DSN, validates the struct, and sets a dynamic default.

// LogLevel is a custom type; Scan lets fuda convert the default/YAML// string into it via the Scanner interface.typeLogLevelintconst (
LevelInfoLogLevel=iotaLevelDebug
)
func (l*LogLevel) Scan(srcany) error {
ifsrc=="debug" {
*l=LevelDebug
}
returnnil
}
typeConfigstruct {
AppNamestring`yaml:"app_name" validate:"required"`Envstring`yaml:"env" default:"dev" validate:"oneof=dev staging prod"`LogLevelLogLevel`yaml:"log_level" default:"info"`Hoststring`yaml:"host" default:"0.0.0.0" env:"APP_HOST"`Portint`yaml:"port" default:"8080" env:"APP_PORT" validate:"min=1,max=65535"`DBUserstring`yaml:"db_user" default:"app"`DBPasswordstring`ref:"file://secrets/db_password.txt"`DBHoststring`yaml:"db_host" default:"localhost"`DBNamestring`yaml:"db_name" default:"orders"`DSNstring`dsn:"postgres://${.DBUser}:${.DBPassword}@${.DBHost}/${.DBName}"`StartedAt time.Time
}
// SetDefaults runs after tags are applied, for defaults tags can't express.func (c*Config) SetDefaults() {
c.StartedAt=time.Now()
}
funcmain() {
varcfgConfigiferr:=fuda.LoadFile("config.yaml", &cfg); err!=nil {
varverr*fuda.ValidationErroriferrors.As(err, &verr) {
log.Fatalf("invalid config: %v", verr.Errors)
}
log.Fatal(err)
}
fmt.Printf("%s DSN: %s\n", cfg.AppName, cfg.DSN)
}

Pair it with a minimal config.yaml:

app_name: orders-apidb_user: app

With secrets/db_password.txt holding the database password, Fuda resolves the ref, composes DSN, applies every default, validates the result, and stamps StartedAt before your program ever sees cfg. APP_PORT=9090 overrides Port at runtime without touching the file.

Feature tour

The struct above already covers files, defaults, env overrides, secrets, DSN composition, validation, dynamic defaults, and custom types. Fuda also handles the rest of a real service's configuration.

Human-readable sizes and durations

typeConfigstruct {
Timeout time.Duration`yaml:"timeout"`Retention fuda.Duration`yaml:"retention"`CacheSize fuda.ByteSize`yaml:"cache_size"`
}
timeout: 30sretention: 7dcache_size: 10MiB

Fuda parses 7d into a duration and 10MiB into a byte count, no manual parsing required. fuda.Duration and fuda.ByteSize also marshal back to a readable string instead of raw nanoseconds or bytes.

Beyond a single file

  • Environment prefixes.WithEnvPrefix("APP_") matches env tags against APP_HOST instead of HOST.
  • Dotenv overlays.WithDotEnvFiles([]string{".env", ".env.local"}) layers environment files before Fuda reads the struct.
  • Templated config.WithTemplate(data) renders the YAML or JSON file as a Go template before parsing it.
  • Live reload.fuda/watcher reloads and revalidates the struct when a watched file or remote reference changes, and delivers the new value on a channel.
  • Vault secrets.fuda/vault resolves ref:"vault:///secret/data/app#password" against a running Vault server, with Kubernetes and AppRole auth built in.

Each of these is a runnable program under examples/.

Tag reference

TagExampleWhat it does
yaml / jsonyaml:"host"Maps a file key to the field.
defaultdefault:"8080"Supplies a value when no other source did.
envenv:"APP_PORT"Reads an environment variable override.
refref:"file:///run/secrets/token"Resolves a fixed external URI (file, HTTP, Vault).
refFromrefFrom:"TokenURI"Resolves the URI stored in another field.
dsndsn:"postgres://${.Host}:5432/app"Builds a string from fields, env values, or refs.
validatevalidate:"required,min=1"Applies a validator rule after loading.

See the full tag reference for dsnStrict and every validator rule.

Learn more

Changelog

See CHANGELOG.md for release history.

License

Fuda is licensed under the Apache License 2.0.

About

Lightweight, struct-tag-first configuration loader for Go with built-in defaults, env overrides, and secret resolution.

Topics

Resources

Code of conduct

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages