Codecs Reference

Built-in codecs for configuration format support and type conversion

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

Codec Interface

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

Codecs handle encoding and decoding of configuration data between different formats.

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:

  • Uses gopkg.in/yaml.v3
  • Supports YAML 1.2 features
  • Handles anchors and aliases
  • Preserves indentation on encoding

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 provide automatic type conversion for getter methods.

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")  // Uses BoolCaster internally

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")  // Uses StringCaster internally

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-
Bool-
Int*-
Uint*-
Float*-
String-
Time-
Duration-

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("myformat", MyCodec{})
    codec.RegisterDecoder("myformat", MyCodec{})
}

Registration functions:

func RegisterEncoder(name string, encoder Codec)
func RegisterDecoder(name string, decoder Codec)

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("myformat", MyCodec{})
    codec.RegisterDecoder("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 _ "yourmodule/xmlcodec"  // Registers custom codec

cfg := config.MustNew(
    config.WithFileAs("config.xml", "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
  • Casters: Minimal overhead, optimized for common cases

Next Steps