Skip to content

Config Module

The config module provides comprehensive configuration file parsing and management, similar to Viper in Go. It supports multiple formats (YAML, TOML, XML, CSV) with full type safety, nested structure access, and environment variable overlay.

Features

  • Multiple Formats: YAML, TOML, XML, and CSV support
  • Auto-Detection: Automatic format detection from file extension
  • Type-Safe Access: Dedicated getters with type validation
  • Dot Notation: Navigate nested structures with simple paths
  • Direct Property Access: Use cfg.key for clean code
  • Default Values: Built-in support for fallback values
  • Environment Variables: Overlay environment variables on config
  • Config Merging: Combine multiple configuration sources
  • Complex Structures: Handle deeply nested maps and arrays

Import

Import
import config

File Reading

ReadFile(filepath string) (map, error)

Reads and parses a configuration file, automatically detecting the format from the file extension.

Supported Extensions: - .yaml / .yml - YAML format - .toml - TOML format - .xml - XML format - .csv - CSV format (returns array of maps)

Example:

ReadFile Example
import config
import fmt

// Read YAML configuration
var cfg, err = config.ReadFile("config.yaml")
if err != None {
    fmt.Printf("Error: %v\n", err)
    return
}

// Access values
var host = cfg.server.host
var port = cfg.server.port
fmt.Printf("Server: %s:%d\n", host, port)

Format-Specific Parsing

ParseYAML(content string) (map, error)

Parses YAML content from a string.

ParseYAML Example
1
2
3
4
5
6
var yamlStr = "name: MyApp\nversion: 1.0.0\nenabled: true"
var cfg, err = config.ParseYAML(yamlStr)
if err == None {
    var name = cfg.name  // "MyApp"
    fmt.Printf("App: %s\n", name)
}

ParseTOML(content string) (map, error)

Parses TOML content from a string.

ParseTOML Example
1
2
3
4
5
var tomlStr = "[server]\nhost = \"localhost\"\nport = 8080"
var cfg, err = config.ParseTOML(tomlStr)
if err == None {
    var host = cfg.server.host  // "localhost"
}

ParseXML(content string) (map, error)

Parses XML content from a string.

ParseXML Example
var xmlStr = "<config><server><host>localhost</host></server></config>"
var cfg, err = config.ParseXML(xmlStr)

ParseCSV(content string) (array, error)

Parses CSV content with automatic header detection and type inference.

ParseCSV Example
1
2
3
4
5
6
7
8
9
var csvData = "name,age,active\nAlice,30,true\nBob,25,false"
var rows, err = config.ParseCSV(csvData)
if err == None {
    for row in rows {
        var name = row.name
        var age = row.age
        fmt.Printf("%s is %d years old\n", name, age)
    }
}

Value Extraction

Two Access Styles

The config module supports two ways to access values:

Direct Dot Access
1
2
3
4
5
6
7
8
9
var cfg, _ = config.ReadFile("config.yaml")

// Direct property access
var version = cfg.version
var host = cfg.server.host
var port = cfg.server.port

// Deep nesting (4+ levels)
var dbPassword = cfg.services.database.environment.POSTGRES_PASSWORD

Advantages: - Clean, readable code - Natural JavaScript/Python-like syntax - Returns None for missing keys

Type-Safe Getters
1
2
3
4
// Type-safe extraction with error handling
var host, err = config.GetString(cfg, "server.host")
var port, err = config.GetInt(cfg, "server.port")
var ssl, err = config.GetBool(cfg, "server.ssl")

Advantages: - Explicit type validation - Error tuples for missing keys - Type conversion included

Get(config map, path string) (any, error)

Retrieves a value from config using dot-notation path.

Get Example
1
2
3
4
var value, err = config.Get(cfg, "database.connection.host")
if err == None {
    fmt.Printf("Value: %v\n", value)
}

GetString(config map, path string) (string, error)

Retrieves a string value with type validation.

GetString Example
1
2
3
4
var hostname, err = config.GetString(cfg, "server.hostname")
if err != None {
    fmt.Printf("Error: %v\n", err)
}

GetInt(config map, path string) (int, error)

Retrieves an integer value with type validation.

GetInt Example
1
2
3
4
var port, err = config.GetInt(cfg, "server.port")
if err != None {
    fmt.Println("Port not found or invalid")
}

GetFloat(config map, path string) (float64, error)

Retrieves a float value with type validation.

GetFloat Example
var timeout, err = config.GetFloat(cfg, "api.timeout")

GetBool(config map, path string) (bool, error)

Retrieves a boolean value with type validation.

GetBool Example
var debug, err = config.GetBool(cfg, "features.debug")

GetArray(config map, path string) (array, error)

Retrieves an array value.

GetArray Example
1
2
3
4
5
6
var servers, err = config.GetArray(cfg, "api.endpoints")
if err == None {
    for server in servers {
        fmt.Printf("Endpoint: %v\n", server)
    }
}

GetMap(config map, path string) (map, error)

Retrieves a nested map.

GetMap Example
1
2
3
4
5
var database, err = config.GetMap(cfg, "database")
if err == None {
    var host = database.host
    var port = database.port
}

Default Values

GetStringOr(config map, path string, default string) string

Returns the string value or a default if not found.

GetStringOr Example
var env = config.GetStringOr(cfg, "environment", "development")
var host = config.GetStringOr(cfg, "server.host", "localhost")

GetIntOr(config map, path string, default int) int

GetIntOr Example
var port = config.GetIntOr(cfg, "server.port", 8080)
var timeout = config.GetIntOr(cfg, "timeout", 30)

GetFloatOr(config map, path string, default float64) float64

GetFloatOr Example
var ratio = config.GetFloatOr(cfg, "performance.ratio", 0.75)

GetBoolOr(config map, path string, default bool) bool

GetBoolOr Example
var debug = config.GetBoolOr(cfg, "debug", false)
var enabled = config.GetBoolOr(cfg, "features.logging", true)

Advanced Features

ReadFileWithEnv(filepath string, prefix string) (map, error)

Reads a config file and overlays environment variables.

ReadFileWithEnv Example
1
2
3
4
5
6
// Reads config.yaml and overlays environment variables
// with prefix APP (e.g., APP_SERVER_HOST, APP_SERVER_PORT)
var cfg, err = config.ReadFileWithEnv("config.yaml", "APP")

// Environment variable APP_SERVER_HOST overrides cfg.server.host
var host = cfg.server.host

Example:

1
2
3
# Set environment variables
export APP_SERVER_HOST=production.example.com
export APP_SERVER_PORT=9000
Environment Override Example
1
2
3
4
// These will use environment values if set
var cfg, _ = config.ReadFileWithEnv("config.yaml", "APP")
var host = cfg.server.host  // "production.example.com" from env
var port = cfg.server.port  // 9000 from env

MergeConfigs(configs ...map) (map, error)

Merges multiple configuration maps. Later configs override earlier ones.

Merge Example
// Base configuration
var baseConfig = {"timeout": 30, "retries": 3}

// Environment-specific overrides
var prodConfig = {"timeout": 60, "retries": 5, "cache": true}

// Merge configurations
var finalConfig, _ = config.MergeConfigs(baseConfig, prodConfig)

// finalConfig: {timeout: 60, retries: 5, cache: true}

Real-World Examples

Example 1: Docker Compose Configuration

docker-compose.yaml:

version: '3.8'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    networks:
      - app-network
  database:
    image: postgres:latest
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db-data:/var/lib/postgresql/data

Parsing with Harneet:

YAML Config Example
import config
import fmt

function main() {
    var cfg, err = config.ReadFile("docker-compose.yaml")
    if err != None {
        fmt.Printf("Error: %v\n", err)
        return
    }

    // Direct dot access (clean and readable)
    var version = cfg.version
    var webImage = cfg.services.web.image
    var dbPassword = cfg.services.database.environment.POSTGRES_PASSWORD

    fmt.Printf("Docker Compose Version: %s\n", version)
    fmt.Printf("Web Image: %s\n", webImage)
    fmt.Printf("Database Password: %s\n", dbPassword)

    // Type-safe getters (with validation)
    var webPorts, _ = config.GetArray(cfg, "services.web.ports")
    var dbVolumes, _ = config.GetArray(cfg, "services.database.volumes")

    fmt.Printf("Web Ports: %v\n", webPorts)
    fmt.Printf("Database Volumes: %v\n", dbVolumes)
}

Example 2: Application Configuration (YAML)

config.yaml:

server:
  host: localhost
  port: 8080
  ssl: true

database:
  host: db.example.com
  port: 5432
  name: myapp
  pool_size: 10

features:
  debug: false
  logging: true
  cache_enabled: true

api:
  timeout: 30
  retries: 3
  endpoints:
    - https://api1.example.com
    - https://api2.example.com

Using the config:

Using YAML Config
import config
import fmt

function main() {
    var cfg, _ = config.ReadFile("config.yaml")

    // Extract server configuration
    var host = cfg.server.host
    var port = cfg.server.port
    var ssl = cfg.server.ssl

    fmt.Printf("Starting server on %s:%d (SSL: %v)\n", host, port, ssl)

    // Database configuration with defaults
    var dbHost = config.GetStringOr(cfg, "database.host", "localhost")
    var dbPort = config.GetIntOr(cfg, "database.port", 5432)
    var poolSize = config.GetIntOr(cfg, "database.pool_size", 5)

    fmt.Printf("Database: %s:%d (pool: %d)\n", dbHost, dbPort, poolSize)

    // Feature flags
    var debug = config.GetBoolOr(cfg, "features.debug", false)
    var logging = config.GetBoolOr(cfg, "features.logging", true)

    if debug {
        fmt.Println("Debug mode enabled")
    }
    if logging {
        fmt.Println("Logging enabled")
    }

    // API endpoints
    var endpoints, _ = config.GetArray(cfg, "api.endpoints")
    fmt.Println("API Endpoints:")
    for endpoint in endpoints {
        fmt.Printf("  - %v\n", endpoint)
    }
}

Example 3: Editor Configuration (TOML)

config.toml:

theme = "dark"

[editor]
line-number = "relative"
rainbow-brackets = true
max-panel-width = 120

[editor.cursor-shape]
insert = "block"
normal = "block"

[editor.lsp]
display-inlay-hints = false
display-messages = true

[keys.normal]
tab = "rotate_view"
C-p = "file_picker"

Reading the configuration:

Reading TOML Config
import config
import fmt

function main() {
    var cfg, _ = config.ReadFile("config.toml")

    // Simple values
    var theme = cfg.theme
    fmt.Printf("Theme: %s\n", theme)

    // Nested configuration (use getters for hyphenated keys)
    var lineNumber, _ = config.GetString(cfg, "editor.line-number")
    var rainbowBrackets, _ = config.GetBool(cfg, "editor.rainbow-brackets")
    var maxWidth, _ = config.GetInt(cfg, "editor.max-panel-width")

    fmt.Printf("Line Numbers: %s\n", lineNumber)
    fmt.Printf("Rainbow Brackets: %v\n", rainbowBrackets)
    fmt.Printf("Max Width: %d\n", maxWidth)

    // Deep nesting
    var insertCursor, _ = config.GetString(cfg, "editor.cursor-shape.insert")
    var normalCursor, _ = config.GetString(cfg, "editor.cursor-shape.normal")

    fmt.Printf("Cursor Shapes - Insert: %s, Normal: %s\n", insertCursor, normalCursor)

    // Key bindings
    var tabAction, _ = config.GetString(cfg, "keys.normal.tab")
    var ctrlP, _ = config.GetString(cfg, "keys.normal.C-p")

    fmt.Printf("Tab: %s, Ctrl-P: %s\n", tabAction, ctrlP)
}

Example 4: Environment Variable Overlay

config.yaml:

1
2
3
4
5
6
server:
  host: localhost
  port: 8080
database:
  host: localhost
  port: 5432

Using environment overlay:

Environment Overlay
import config
import fmt

function main() {
    // Read config with APP_ prefix for environment variables
    // Environment: APP_SERVER_HOST=production.com, APP_SERVER_PORT=443
    var cfg, _ = config.ReadFileWithEnv("config.yaml", "APP")

    // These will use environment values if set
    var serverHost = cfg.server.host  // "production.com" from APP_SERVER_HOST
    var serverPort = cfg.server.port  // 443 from APP_SERVER_PORT

    fmt.Printf("Server: %s:%d\n", serverHost, serverPort)
}

Example 5: Multi-Environment Configuration

Multi-Environment Config
import config
import os
import fmt

function main() {
    // Load base configuration
    var baseConfig, _ = config.ReadFile("config.base.yaml")

    // Load environment-specific configuration
    var env, _ = os.Getenv("ENVIRONMENT")
    if env == "" {
        env = "development"
    }

    var envConfigPath = "config." + env + ".yaml"
    var envConfig, err = config.ReadFile(envConfigPath)

    var finalConfig = baseConfig
    if err == None {
        // Merge environment-specific overrides
        finalConfig, _ = config.MergeConfigs(baseConfig, envConfig)
    }

    // Overlay environment variables
    finalConfig, _ = config.ReadFileWithEnv("config.base.yaml", "APP")

    // Use the final merged configuration
    var host = finalConfig.server.host
    var port = finalConfig.server.port

    fmt.Printf("Running on %s:%d (env: %s)\n", host, port, env)
}

Best Practices

1. Use Direct Dot Access for Simple Cases

Use Dot Access
1
2
3
4
// Clean and readable
var host = cfg.server.host
var port = cfg.server.port
var ssl = cfg.server.ssl

2. Use Type-Safe Getters for Validation

Type-Safe Getters for Validation
1
2
3
4
5
6
// When you need explicit error handling
var timeout, err = config.GetInt(cfg, "api.timeout")
if err != None {
    fmt.Println("Timeout not configured, using default")
    timeout = 30
}

3. Always Provide Defaults

Always Provide Defaults
1
2
3
4
// Use GetXOr functions for resilient code
var host = config.GetStringOr(cfg, "server.host", "localhost")
var port = config.GetIntOr(cfg, "server.port", 8080)
var debug = config.GetBoolOr(cfg, "debug", false)

4. Handle Missing Keys Gracefully

Handle Missing Keys
1
2
3
4
5
6
var optionalFeature = cfg.features.experimental
if optionalFeature == None {
    fmt.Println("Experimental features not configured")
} else {
    // Use the feature
}

5. Use Environment Overlays for Deployment

Environment Overlays
1
2
3
// Development: use config file values
// Production: override with environment variables
var cfg, _ = config.ReadFileWithEnv("config.yaml", "APP")

Error Handling

The config module follows Harneet's error handling patterns:

Error Handling
// Pattern 1: Check error tuple
var cfg, err = config.ReadFile("config.yaml")
if err != None {
    fmt.Printf("Failed to read config: %v\n", err)
    return
}

// Pattern 2: Use defaults
var host = config.GetStringOr(cfg, "server.host", "localhost")

// Pattern 3: Check for None
var optional = cfg.optional.feature
if optional == None {
    fmt.Println("Optional feature not configured")
}

Type Inference

The config module automatically infers types from values:

  • Boolean: true, false
  • Integer: 42, 8080, -100
  • Float: 3.14, 0.5
  • String: Everything else
CSV Type Inference
1
2
3
4
5
6
7
// CSV parsing with automatic type inference
var csvData = "name,age,active\nAlice,30,true\nBob,25,false"
var rows, _ = config.ParseCSV(csvData)

var firstRow = rows[0]
var age = firstRow.age      // Automatically inferred as integer (30)
var active = firstRow.active // Automatically inferred as boolean (true)

Summary

The config module provides a comprehensive, Viper-like configuration management system for Harneet:

  • Multiple formats: YAML, TOML, XML, CSV
  • Two access styles: Direct dot access and type-safe getters
  • Deep nesting: 5+ levels of nested structures
  • Type safety: Automatic type conversion and validation
  • Defaults: Built-in fallback support
  • Environment overlays: Seamless environment variable integration
  • Config merging: Combine multiple sources
  • Production-ready: Handles complex real-world configurations

Perfect for building applications with flexible, maintainable configuration management!