YAML Config Decoder

Decode YAML into typed structs with unknown-field rejection, then validate required addresses, durations, and size limits.

GoConfig Management

Code

snippet
package main

import (
	"bytes"
	"fmt"
	"time"

	"gopkg.in/yaml.v3"
)

type ServerConfig struct {
	Addr         string `yaml:"addr"`
	ReadTimeout  string `yaml:"read_timeout"`
	MaxBodyBytes int64  `yaml:"max_body_bytes"`
}

type Config struct {
	Server   ServerConfig `yaml:"server"`
	Features []string     `yaml:"features"`
	Debug    bool         `yaml:"debug"`
}

func LoadConfig(data []byte) (Config, error) {
	var cfg Config
	decoder := yaml.NewDecoder(bytes.NewReader(data))
	decoder.KnownFields(true)
	if err := decoder.Decode(&cfg); err != nil {
		return Config{}, fmt.Errorf("decode config: %w", err)
	}
	if cfg.Server.Addr == "" {
		return Config{}, fmt.Errorf("server.addr is required")
	}
	if _, err := time.ParseDuration(cfg.Server.ReadTimeout); err != nil {
		return Config{}, fmt.Errorf("server.read_timeout: %w", err)
	}
	if cfg.Server.MaxBodyBytes <= 0 {
		return Config{}, fmt.Errorf("server.max_body_bytes must be positive")
	}
	return cfg, nil
}

func main() {
	document := []byte(`
server:
  addr: ":8080"
  read_timeout: 5s
  max_body_bytes: 1048576
features:
  - search
  - preview
debug: false
`)
	cfg, err := LoadConfig(document)
	fmt.Printf("config: %+v err=%v\n", cfg, err)

	unknown := []byte("server:\n  addr: \":8080\"\n  read_timeout: 5s\n  max_body_bytes: 10\n  tls: true\n")
	_, err = LoadConfig(unknown)
	fmt.Println("unknown field rejected:", err != nil)
}