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
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)
|
ParseYAML(content string) (map, error)
Parses YAML content from a string.
| ParseYAML Example |
|---|
| 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 |
|---|
| 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 |
|---|
| 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)
}
}
|
Two Access Styles
The config module supports two ways to access values:
1. Direct Dot Access (Recommended for Simple Cases)
| Direct Dot Access |
|---|
| 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
2. Type-Safe Getters (Recommended for Validation)
| Type-Safe Getters |
|---|
| // 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 |
|---|
| 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 |
|---|
| 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 |
|---|
| 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 |
|---|
| 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 |
|---|
| 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 |
|---|
| // 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:
| # Set environment variables
export APP_SERVER_HOST=production.example.com
export APP_SERVER_PORT=9000
|
| Environment Override Example |
|---|
| // 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:
| 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 |
|---|
| // 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 |
|---|
| // 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 |
|---|
| // 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 |
|---|
| 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 |
|---|
| // 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 |
|---|
| // 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!