Codecs Reference

Built-in codecs for configuration format support and type conversion

Complete reference for built-in codecs and guidance on creating custom codecs.

Encoder and Decoder

The rivaas.dev/config/codec package registers implementations by name (codec.Type). Two interfaces are used:

type Encoder interface {
    Encode(v any) ([]byte, error)
}

type Decoder interface {
    Decode(data []byte, v any) error
}

Format codecs (JSON, YAML, TOML) implement both and are registered for Encode and Decode. Caster types are registered as decoders only (see below).

Built-in Format Codecs

JSON Codec

Type: codec.TypeJSON
Import: rivaas.dev/config/codec

Handles JSON format encoding and decoding.

Capabilities:

  • ✅ Encode
  • ✅ Decode

File extensions:

  • .json

Example:

import "rivaas.dev/config/codec"

cfg := config.MustNew(
    config.WithFileAs("config.txt", codec.TypeJSON),
)

Features:

  • Standard Go encoding/json implementation
  • Preserves JSON types (numbers, strings, booleans, arrays, objects)
  • Pretty-printed output when encoding

YAML Codec

Type: codec.TypeYAML
Import: rivaas.dev/config/codec

Handles YAML format encoding and decoding.

Capabilities:

  • ✅ Encode
  • ✅ Decode

File extensions:

  • .yaml
  • .yml

Example:

cfg := config.MustNew(
    config.WithFile("config.yaml"),
)

Features:

Common YAML types:

string_value: "hello"
number_value: 42
boolean_value: true
duration_value: 30s
list_value:
  - item1
  - item2
map_value:
  key1: value1
  key2: value2

TOML Codec

Type: codec.TypeTOML
Import: rivaas.dev/config/codec

Handles TOML format encoding and decoding.

Capabilities:

  • ✅ Encode
  • ✅ Decode

File extensions:

  • .toml

Example:

cfg := config.MustNew(
    config.WithFile("config.toml"),
)

Features:

Sample TOML:

[server]
host = "localhost"
port = 8080

[database]
host = "db.example.com"
port = 5432

Environment Variable Codec

Type: codec.TypeEnvVar
Import: rivaas.dev/config/codec

Handles environment variable format.

Capabilities:

  • ❌ Encode (returns error)
  • ✅ Decode

Example:

cfg := config.MustNew(
    config.WithEnv("APP_"),
)

Format:

PREFIX_SECTION_KEY=value
PREFIX_A_B_C=nested

Transformation:

  • Strips prefix
  • Converts to lowercase
  • Splits by underscores
  • Creates nested structure

See: Environment Variables Guide

Built-in Caster Codecs

Caster codecs are decoder registrations in the codec registry (RegisterDecoder only). They are useful when you plug decoders into custom pipelines.

Config getters (String, Int, Bool, Duration, and so on) use github.com/spf13/cast on merged values—they do not call codec.GetDecoder(TypeCaster…) for each read.

Boolean Caster

Type: codec.TypeCasterBool

Converts values to bool.

Supported inputs:

  • true, "true", "True", "TRUE", 1, "1"true
  • false, "false", "False", "FALSE", 0, "0"false

Example:

debug := cfg.Bool("debug")

Integer Casters

Convert values to integer types.

TypeCodecTarget Type
codec.TypeCasterIntIntint
codec.TypeCasterInt8Int8int8
codec.TypeCasterInt16Int16int16
codec.TypeCasterInt32Int32int32
codec.TypeCasterInt64Int64int64

Supported inputs:

  • Integer values: 42, 100
  • String integers: "42", "100"
  • Float values: 42.042
  • String floats: "42.0"42

Example:

port := cfg.Int("server.port")      // Uses IntCaster
timeout := cfg.Int64("timeout_ms")  // Uses Int64Caster

Unsigned Integer Casters

Convert values to unsigned integer types.

TypeCodecTarget Type
codec.TypeCasterUintUintuint
codec.TypeCasterUint8Uint8uint8
codec.TypeCasterUint16Uint16uint16
codec.TypeCasterUint32Uint32uint32
codec.TypeCasterUint64Uint64uint64

Supported inputs:

  • Positive integers: 42, 100
  • String integers: "42", "100"

Float Casters

Convert values to floating-point types.

TypeCodecTarget Type
codec.TypeCasterFloat32Float32float32
codec.TypeCasterFloat64Float64float64

Supported inputs:

  • Float values: 3.14, 2.5
  • String floats: "3.14", "2.5"
  • Integer values: 4242.0
  • String integers: "42"42.0

Example:

ratio := cfg.Float64("ratio")

String Caster

Type: codec.TypeCasterString

Converts any value to string.

Supported inputs:

  • String values: "hello""hello"
  • Numbers: 42"42"
  • Booleans: true"true"
  • Any value with String() method

Example:

value := cfg.String("key")

Time Caster

Type: codec.TypeCasterTime

Converts values to time.Time.

Supported inputs:

  • RFC3339 strings: "2025-01-01T00:00:00Z"
  • ISO8601 strings: "2025-01-01T00:00:00+00:00"
  • Unix timestamps: 1672531200

Example:

createdAt := cfg.Time("created_at")

Formats tried (in order):

  1. time.RFC3339 - "2006-01-02T15:04:05Z07:00"
  2. time.RFC3339Nano - "2006-01-02T15:04:05.999999999Z07:00"
  3. "2006-01-02" - Date only
  4. Unix timestamp (integer)

Duration Caster

Type: codec.TypeCasterDuration

Converts values to time.Duration.

Supported inputs:

  • Duration strings: "30s", "5m", "1h", "2h30m"
  • Integer nanoseconds: 3000000000030s
  • Float seconds: 2.52.5s

Example:

timeout := cfg.Duration("timeout")  // "30s" → 30 * time.Second

Duration units:

  • ns - nanoseconds
  • us or µs - microseconds
  • ms - milliseconds
  • s - seconds
  • m - minutes
  • h - hours

Codec Capabilities Table

CodecEncodeDecodeAuto-DetectExtensions
JSON.json
YAML.yaml, .yml
TOML.toml
EnvVar-
Caster types (Bool, Int*, …)-

Format Auto-Detection

The config package automatically detects formats based on file extensions:

cfg := config.MustNew(
    config.WithFile("config.json"),  // Auto-detects JSON
    config.WithFile("config.yaml"),  // Auto-detects YAML
    config.WithFile("config.toml"),  // Auto-detects TOML
)

Detection rules:

  1. Check file extension
  2. Look up registered decoder for that extension
  3. Use codec if found, error if not

Override auto-detection:

cfg := config.MustNew(
    config.WithFileAs("settings.txt", codec.TypeYAML),
)

Custom Codecs

Registering Custom Codecs

import "rivaas.dev/config/codec"

func init() {
    codec.RegisterEncoder(codec.Type("myformat"), MyCodec{})
    codec.RegisterDecoder(codec.Type("myformat"), MyCodec{})
}

Registration functions:

func RegisterEncoder(name codec.Type, encoder codec.Encoder)
func RegisterDecoder(name codec.Type, decoder codec.Decoder)

Custom Codec Example

type MyCodec struct{}

func (c MyCodec) Encode(v any) ([]byte, error) {
    data, ok := v.(map[string]any)
    if !ok {
        return nil, fmt.Errorf("expected map[string]any, got %T", v)
    }
    
    // Your encoding logic
    var buf bytes.Buffer
    // ... write to buf ...
    
    return buf.Bytes(), nil
}

func (c MyCodec) Decode(data []byte, v any) error {
    target, ok := v.(*map[string]any)
    if !ok {
        return fmt.Errorf("expected *map[string]any, got %T", v)
    }
    
    // Your decoding logic
    result := make(map[string]any)
    // ... parse data into result ...
    
    *target = result
    return nil
}

func init() {
    codec.RegisterEncoder(codec.Type("myformat"), MyCodec{})
    codec.RegisterDecoder(codec.Type("myformat"), MyCodec{})
}

See: Custom Codecs Guide

Common Patterns

Pattern 1: Mixed Formats

cfg := config.MustNew(
    config.WithFile("config.yaml"),  // YAML
    config.WithFile("secrets.json"), // JSON
    config.WithFile("extra.toml"),   // TOML
)

Pattern 2: Explicit Format

cfg := config.MustNew(
    config.WithFileAs("config.txt", codec.TypeYAML),
)

Pattern 3: Content Source

yamlData := []byte(`server: {port: 8080}`)
cfg := config.MustNew(
    config.WithContent(yamlData, codec.TypeYAML),
)

Pattern 4: Custom Codec

import (
    "rivaas.dev/config"
    "rivaas.dev/config/codec"
    _ "yourmodule/xmlcodec" // registers custom codec in init()
)

cfg := config.MustNew(
    config.WithFileAs("config.xml", codec.Type("xml")),
)

Type Conversion Examples

String to Duration

timeout: "30s"
timeout := cfg.Duration("timeout")  // 30 * time.Second

String to Int

port: "8080"
port := cfg.Int("port")  // 8080

String to Bool

debug: "true"
debug := cfg.Bool("debug")  // true

String to Time

created: "2025-01-01T00:00:00Z"
created := cfg.Time("created")  // time.Time

Error Handling

Decode Errors

if err := cfg.Load(context.Background()); err != nil {
    // Error format:
    // "config error in source[0] during load: yaml: unmarshal error"
    log.Printf("Failed to decode: %v", err)
}

Encode Errors

if err := cfg.Dump(context.Background()); err != nil {
    // Error format:
    // "config error in dumper[0] during dump: json: unsupported type"
    log.Printf("Failed to encode: %v", err)
}

Type Conversion Errors

// For error-returning methods
port, err := config.GetE[int](cfg, "server.port")
if err != nil {
    log.Printf("Invalid port: %v", err)
}

Performance Notes

  • JSON: Fast, minimal overhead
  • YAML: Moderate overhead (parsing complexity)
  • TOML: Fast, strict typing
  • Registry caster decoders: small overhead when used in custom decode paths; getters use spf13/cast separately

Next Steps