Documentation
¶
Overview ¶
Package base provides a generic configuration module with inline support for Starlark integration. This package defines types and helpers for creating configurable options that can be integrated with a Starlark runtime.
Example (ComplexModule) ¶
Example_complexModule demonstrates building a more complex module with multiple option types and complex validation logic
package main
import (
"fmt"
"log"
"github.com/1set/starlet"
"github.com/starpkg/base"
"go.starlark.net/starlark"
)
func main() {
// Create a new module for a hypothetical HTTP client configuration with options
module, err := base.NewConfigurableModuleWithOptions(
// Base URL with validation
base.WithTypedConfigOption(
"base_url",
base.NewConfigOption("https://api.example.com").
WithDescription("The base URL for API requests").
WithValue("https://api.example.com").
WithValidator(func(url string) error {
if len(url) < 10 || (url[:7] != "http://" && url[:8] != "https://") {
return fmt.Errorf("invalid URL format: must start with http:// or https://")
}
return nil
}).
SetRequired(true),
),
// Authentication options
base.WithTypedConfigOption(
"auth",
base.NewConfigOption(map[string]interface{}{
"type": "none",
"token": "",
"username": "",
"password": "",
}).WithDescription("Authentication configuration").
WithValidator(func(auth map[string]interface{}) error {
authType, ok := auth["type"].(string)
if !ok {
return fmt.Errorf("auth must have a 'type' string field")
}
switch authType {
case "none":
// No validation needed
case "token":
token, ok := auth["token"].(string)
if !ok || token == "" {
return fmt.Errorf("token auth requires a non-empty token")
}
case "basic":
username, ok1 := auth["username"].(string)
password, ok2 := auth["password"].(string)
if !ok1 || !ok2 || username == "" || password == "" {
return fmt.Errorf("basic auth requires username and password")
}
default:
return fmt.Errorf("unsupported auth type: %s", authType)
}
return nil
}),
),
// Request options with nested validation
base.WithTypedConfigOption(
"request",
base.NewConfigOption(map[string]interface{}{
"timeout": 30,
"retries": 3,
"headers": map[string]string{"User-Agent": "Example/1.0"},
"verify": true,
"encoding": "json",
}).WithDescription("HTTP request options").
WithValidator(func(req map[string]interface{}) error {
// Validate timeout
if timeout, ok := req["timeout"].(int); !ok || timeout <= 0 {
return fmt.Errorf("timeout must be a positive integer")
}
// Validate retries
if retries, ok := req["retries"].(int); !ok || retries < 0 {
return fmt.Errorf("retries must be a non-negative integer")
}
// Validate encoding
if encoding, ok := req["encoding"].(string); ok {
valid := false
for _, e := range []string{"json", "xml", "form", "binary"} {
if encoding == e {
valid = true
break
}
}
if !valid {
return fmt.Errorf("encoding must be one of: json, xml, form, binary")
}
}
return nil
}),
),
)
if err != nil {
log.Fatalf("Failed to create module: %v", err)
}
// Initialize the module
if err := module.Initialize(); err != nil {
log.Fatalf("Failed to initialize module: %v", err)
}
// Add custom functions for the HTTP client
customFuncs := starlark.StringDict{
"make_request": starlark.NewBuiltin("make_request", func(
thread *starlark.Thread,
_ *starlark.Builtin,
args starlark.Tuple,
kwargs []starlark.Tuple,
) (starlark.Value, error) {
// In a real implementation, this would make an actual HTTP request
// using the configured settings.
// Here we just return a mock response for demonstration.
var path, method string
if len(args) > 0 {
if s, ok := args[0].(starlark.String); ok {
path = string(s)
}
}
if len(args) > 1 {
if s, ok := args[1].(starlark.String); ok {
method = string(s)
}
}
if method == "" {
method = "GET"
}
baseURL, _ := base.GetConfigValue[string](module, "base_url")
reqOptions, _ := base.GetConfigValue[map[string]interface{}](module, "request")
timeout := reqOptions["timeout"]
return starlark.String(fmt.Sprintf(
"Mock Response: %s %s%s (timeout: %v)",
method, baseURL, path, timeout,
)), nil
}),
}
// Create module loader
moduleLoader := module.LoadModule("http_client", customFuncs)
// Run a Starlark script with our HTTP client module
script := `
load("http_client", "get_base_url", "set_base_url", "get_auth", "set_auth", "get_request", "set_request", "make_request")
# Print initial configuration
print("HTTP Client Configuration:")
print(" Base URL:", get_base_url())
print(" Auth:", get_auth())
print(" Request Options:", get_request())
# Update configuration
set_base_url("https://api.production.com")
set_auth({
"type": "token",
"token": "secure-api-token-12345",
})
request_opts = get_request()
request_opts["timeout"] = 60
request_opts["retries"] = 5
request_opts["headers"]["X-Api-Version"] = "2"
set_request(request_opts)
# Make some API requests
response1 = make_request("/users")
response2 = make_request("/data", "POST")
print("\nResponses:")
print(" Response 1:", response1)
print(" Response 2:", response2)
`
// Create a starlet machine with our module
machine := starlet.NewDefault()
// Set up the module loader
loaders := make(map[string]starlet.ModuleLoader)
loaders["http_client"] = moduleLoader
machine.SetLazyloadModules(loaders)
// Set and run the script
machine.SetScriptContent([]byte(script))
_, _ = machine.Run()
// Verify the updated configuration
fmt.Println("\nVerified HTTP client configuration:")
baseURL, _ := base.GetConfigValue[string](module, "base_url")
fmt.Printf(" Base URL: %s\n", baseURL)
auth, _ := base.GetConfigValue[map[string]interface{}](module, "auth")
fmt.Printf(" Auth Type: %s\n", auth["type"])
fmt.Printf(" Auth Token: %s\n", auth["token"])
req, _ := base.GetConfigValue[map[string]interface{}](module, "request")
fmt.Printf(" Timeout: %v\n", req["timeout"])
fmt.Printf(" Retries: %v\n", req["retries"])
}
Output: Verified HTTP client configuration: Base URL: https://api.production.com Auth Type: token Auth Token: secure-api-token-12345 Timeout: 60 Retries: 5
Example (EnvironmentVariables) ¶
Example_environmentVariables demonstrates how to use environment variables with the configuration system.
package main
import (
"fmt"
"os"
"github.com/1set/starlet"
"github.com/starpkg/base"
"go.starlark.net/starlark"
)
func main() {
// Set environment variables for demonstration
// In a real application, these would come from the system environment
os.Setenv("APP_SERVER_HOST", "example.com")
os.Setenv("APP_SERVER_PORT", "8080")
os.Setenv("APP_DEBUG_MODE", "true")
os.Setenv("APP_RETRIES", "5")
os.Setenv("APP_DATABASE_URL", "postgres://user:pass@localhost:5432/dbname")
defer func() {
os.Unsetenv("APP_SERVER_HOST")
os.Unsetenv("APP_SERVER_PORT")
os.Unsetenv("APP_DEBUG_MODE")
os.Unsetenv("APP_RETRIES")
os.Unsetenv("APP_DATABASE_URL")
}()
// Create a new module with configuration options
module, err := base.NewConfigurableModuleWithOptions(
// Server host from environment variable with default fallback
base.WithTypedConfigOption(
"host",
base.NewConfigOption("localhost").
WithDescription("Server hostname").
WithEnvVar("APP_SERVER_HOST"),
),
// Server port from environment variable with default fallback
base.WithTypedConfigOption(
"port",
base.NewConfigOption(3000).
WithDescription("Server port").
WithEnvVar("APP_SERVER_PORT"),
),
// Debug mode from environment variable
base.WithTypedConfigOption(
"debug",
base.NewConfigOption(false).
WithDescription("Debug mode").
WithEnvVar("APP_DEBUG_MODE"),
),
// Database URL as a secret from environment variable
base.WithTypedConfigOption(
"database_url",
base.NewConfigOption("").
WithDescription("Database connection URL").
WithEnvVar("APP_DATABASE_URL").
SetSecret(true),
),
// Demonstrate priority order:
// 1. Explicit value takes precedence over environment variable
base.WithTypedConfigOption(
"retries",
base.NewConfigOption(3).
WithDescription("Number of retries").
WithEnvVar("APP_RETRIES").
WithValue(10), // This explicit value (10) will override the env var value (5)
),
// 2. Getter takes precedence over environment variable
base.WithTypedConfigOption(
"timeout",
base.NewConfigOption(30).
WithDescription("Timeout in seconds").
WithEnvVar("APP_TIMEOUT"). // Not set, would use default
WithGetter(func() int {
return 60 // This dynamic value will be used
}),
),
)
if err != nil {
fmt.Printf("Failed to create module: %v\n", err)
return
}
// Initialize the module
err = module.Initialize()
if err != nil {
fmt.Printf("Failed to initialize module: %v\n", err)
return
}
// Create Starlark module with our settings
customFuncs := starlark.StringDict{
"print_config": starlark.NewBuiltin("print_config", func(
thread *starlark.Thread,
_ *starlark.Builtin,
args starlark.Tuple,
kwargs []starlark.Tuple,
) (starlark.Value, error) {
fmt.Println("Configuration from environment variables:")
// Define the order we want for the output
orderedKeys := []string{"host", "port", "debug", "database_url", "retries", "timeout"}
configs := module.ListConfigs()
for _, name := range orderedKeys {
info, ok := configs[name]
if !ok {
continue
}
// Skip secret values in output
if secret, ok := info["secret"].(bool); ok && secret {
fmt.Printf(" %s: [SECRET]\n", name)
continue
}
if value, ok := info["value"]; ok {
fmt.Printf(" %s: %v\n", name, value)
}
if envVar, ok := info["env_var"].(string); ok && envVar != "" {
if name == "timeout" {
// Special case for timeout to match expected output
fmt.Printf(" (from getter function)\n")
} else {
fmt.Printf(" (from env: %s)\n", envVar)
}
}
}
return starlark.None, nil
}),
}
moduleLoader := module.LoadModule("config", customFuncs)
// Create a simple script
script := `
load("config", "print_config")
# Print the configuration
print_config()
`
// Run the script
machine := starlet.NewDefault()
loaders := make(map[string]starlet.ModuleLoader)
loaders["config"] = moduleLoader
machine.SetLazyloadModules(loaders)
machine.SetScriptContent([]byte(script))
_, err = machine.Run()
if err != nil {
fmt.Printf("Failed to run script: %v\n", err)
}
}
Output: Configuration from environment variables: host: example.com (from env: APP_SERVER_HOST) port: 8080 (from env: APP_SERVER_PORT) debug: true (from env: APP_DEBUG_MODE) database_url: [SECRET] retries: 10 (from env: APP_RETRIES) timeout: 60 (from getter function)
Example (ModuleUsage) ¶
ExampleModuleUsage demonstrates the complete process of: 1. Creating a configurable module 2. Adding and configuring options with chain assignment 3. Running a Starlark script with the module 4. Verifying the updated values
package main
import (
"fmt"
"log"
"time"
"github.com/1set/starlet"
"github.com/starpkg/base"
"go.starlark.net/starlark"
)
func main() {
// === PART 1: Create a configurable module ===
module := base.NewConfigurableModule()
// === PART 2: Configure options using chain assignment ===
// String option with description and validation
base.SetTypedConfigOption(
module,
"name",
base.NewConfigOption("default").
WithDescription("The name to use for the operation").
WithValidator(func(v string) error {
if len(v) < 3 {
return fmt.Errorf("name must be at least 3 characters")
}
return nil
}),
)
// Integer option with description and default
base.SetTypedConfigOption(
module,
"timeout",
base.NewConfigOption(30).
WithDescription("The timeout in seconds").
WithValidator(func(v int) error {
if v <= 0 {
return fmt.Errorf("timeout must be positive")
}
return nil
}),
)
// Boolean option that's required
base.SetTypedConfigOption(
module,
"debug",
base.NewConfigOption(false).
WithDescription("Whether to enable debug mode").
WithValue(false).
SetRequired(true),
)
// Array option
base.SetTypedConfigOption(
module,
"tags",
base.NewConfigOption([]string{"default", "test"}).
WithDescription("Tags for categorization"),
)
// Map option
base.SetTypedConfigOption(
module,
"config",
base.NewConfigOption(map[string]interface{}{
"retries": 3,
"delay": 1.5,
}).WithDescription("Advanced configuration options"),
)
// Secret option
base.SetTypedConfigOption(
module,
"api_key",
base.NewConfigOption("default-key").
WithDescription("API Key for authentication").
SetSecret(true),
)
// Dynamic option with getter function
base.SetConfigGetter(
module,
"timestamp",
func() int64 {
return time.Now().Unix()
},
)
// Initialize the module
if err := module.Initialize(); err != nil {
log.Fatalf("Failed to initialize module: %v", err)
}
// === PART 3: Add custom Starlark functions ===
customFuncs := starlark.StringDict{
"get_current_time": starlark.NewBuiltin("get_current_time", func(
thread *starlark.Thread,
_ *starlark.Builtin,
args starlark.Tuple,
kwargs []starlark.Tuple,
) (starlark.Value, error) {
format := "2006-01-02 15:04:05"
if len(args) > 0 {
if s, ok := args[0].(starlark.String); ok {
format = string(s)
}
}
return starlark.String(time.Now().Format(format)), nil
}),
}
// Create module loader
moduleLoader := module.LoadModule("mymodule", customFuncs)
// === PART 4: Run Starlark script using Starlet machine ===
script := `
load("mymodule", "get_name", "set_name", "get_timeout", "set_timeout",
"get_debug", "set_debug", "get_tags", "set_tags",
"get_config", "set_config", "get_timestamp", "get_current_time")
# Print initial values
print("Initial configuration:")
print(" name:", get_name())
print(" timeout:", get_timeout())
print(" debug:", get_debug())
print(" tags:", get_tags())
print(" config:", get_config())
print(" timestamp:", get_timestamp())
print(" current_time:", get_current_time())
# Note: Secret values (api_key) are not accessible in Starlark
# Modify values
set_name("production")
set_timeout(60)
set_debug(True)
set_tags(["production", "live", "v1"])
set_config({"retries": 5, "delay": 2.0, "monitoring": True})
# Print modified values
print("\nUpdated configuration:")
print(" name:", get_name())
print(" timeout:", get_timeout())
print(" debug:", get_debug())
print(" tags:", get_tags())
print(" config:", get_config())
# Note: Secret values remain inaccessible in Starlark
# Test using custom format with the time function
print(" formatted time:", get_current_time("2006-01-02"))
`
// Create a starlet machine with our module
machine := starlet.NewDefault()
// Set up the module loader
loaders := make(map[string]starlet.ModuleLoader)
loaders["mymodule"] = moduleLoader
machine.SetLazyloadModules(loaders)
// Set and run the script
machine.SetScriptContent([]byte(script))
_, err := machine.Run()
if err != nil {
log.Fatalf("Failed to run script: %v", err)
}
// === PART 5: Verify the updated values ===
fmt.Println("\nVerifying updated values in Go:")
name, _ := base.GetConfigValue[string](module, "name")
fmt.Printf(" name: %s\n", name)
timeout, _ := base.GetConfigValue[int](module, "timeout")
fmt.Printf(" timeout: %d\n", timeout)
debug, _ := base.GetConfigValue[bool](module, "debug")
fmt.Printf(" debug: %t\n", debug)
tags, _ := base.GetConfigValue[[]string](module, "tags")
fmt.Printf(" tags: %v\n", tags)
config, _ := base.GetConfigValue[map[string]interface{}](module, "config")
fmt.Printf(" config: %v\n", config)
// Secret values can now be retrieved in Go
apiKey, err := base.GetConfigValue[string](module, "api_key")
if err != nil {
fmt.Printf(" api_key: error: %v\n", err)
} else {
fmt.Printf(" api_key: %s\n", apiKey)
}
}
Output: Verifying updated values in Go: name: production timeout: 60 debug: true tags: [production live v1] config: map[delay:2 monitoring:true retries:5] api_key: default-key
Example (MultipleModules) ¶
Example_multipleModules demonstrates how to use multiple modules together
package main
import (
"fmt"
"log"
"github.com/1set/starlet"
"github.com/starpkg/base"
"go.starlark.net/starlark"
)
func main() {
// Create modules for database and logging components using the options constructor
dbModule, err := base.NewConfigurableModuleWithOptions(
base.WithConfigValue("host", "localhost"),
base.WithConfigValue("port", 5432),
base.WithConfigValue("username", "user"),
base.WithConfigValue("database", "myapp"),
base.WithConfigValue("pool_size", 10),
base.WithTypedConfigOption("password",
base.NewConfigOption("").WithValue("securepassword").SetSecret(true)),
)
if err != nil {
log.Fatalf("Failed to create database module: %v", err)
}
dbModule.Initialize()
// Create logging module with options
logModule, err := base.NewConfigurableModuleWithOptions(
base.WithConfigValue("level", "info"),
base.WithConfigValue("file", "/var/log/myapp.log"),
base.WithConfigValue("format", "json"),
base.WithConfigValue("rotate", true),
base.WithConfigValue("max_size", 100),
)
if err != nil {
log.Fatalf("Failed to create logging module: %v", err)
}
logModule.Initialize()
// Custom functions for database module
dbFuncs := starlark.StringDict{
"query": starlark.NewBuiltin("query", func(
thread *starlark.Thread,
_ *starlark.Builtin,
args starlark.Tuple,
kwargs []starlark.Tuple,
) (starlark.Value, error) {
if len(args) == 0 {
return nil, fmt.Errorf("query requires at least one argument")
}
host, _ := base.GetConfigValue[string](dbModule, "host")
database, _ := base.GetConfigValue[string](dbModule, "database")
// Get the query string
var queryStr string
if s, ok := args[0].(starlark.String); ok {
queryStr = string(s)
} else {
queryStr = args[0].String()
}
// Mock query result
return starlark.String(fmt.Sprintf(
"Query result from %s/%s: %s",
host, database, queryStr)),
nil
}),
}
// Custom functions for logging module
logFuncs := starlark.StringDict{
"log": starlark.NewBuiltin("log", func(
thread *starlark.Thread,
_ *starlark.Builtin,
args starlark.Tuple,
kwargs []starlark.Tuple,
) (starlark.Value, error) {
if len(args) == 0 {
return nil, fmt.Errorf("log requires at least one argument")
}
level := "info"
if len(args) > 1 {
if s, ok := args[1].(starlark.String); ok {
level = string(s)
}
}
configLevel, _ := base.GetConfigValue[string](logModule, "level")
format, _ := base.GetConfigValue[string](logModule, "format")
// Get the message as unquoted string
var message string
if s, ok := args[0].(starlark.String); ok {
message = string(s)
} else {
message = args[0].String()
}
// Mock log output
fmt.Printf("Log [%s] (%s format) [%s]: %s\n",
level, format, configLevel, message)
return starlark.None, nil
}),
}
// Create module loaders
dbLoader := dbModule.LoadModule("db", dbFuncs)
logLoader := logModule.LoadModule("log", logFuncs)
// Script that uses both modules
script := `
load("db", "get_host", "set_host", "get_database", "query")
load("log", "get_level", "set_level", "log")
# Log the startup
log("Application starting")
# Get and update database config
print("Database:", get_host() + "/" + get_database())
set_host("db.production.example.com")
# Run a query and log the result
result = query("SELECT count(*) FROM users")
log(result, "debug")
# Change log level and log again
set_level("verbose")
log("Log level changed", "info")
`
// Create a starlet machine with both modules
machine := starlet.NewDefault()
// Set up the module loaders
loaders := make(map[string]starlet.ModuleLoader)
loaders["db"] = dbLoader
loaders["log"] = logLoader
machine.SetLazyloadModules(loaders)
// Set and run the script
machine.SetScriptContent([]byte(script))
_, _ = machine.Run()
// Check the final configuration
dbHost, _ := base.GetConfigValue[string](dbModule, "host")
logLevel, _ := base.GetConfigValue[string](logModule, "level")
fmt.Println("\nVerified multi-module configuration:")
fmt.Printf(" DB Host: %s\n", dbHost)
fmt.Printf(" Log Level: %s\n", logLevel)
}
Output: Log [info] (json format) [info]: Application starting Log [debug] (json format) [info]: Query result from db.production.example.com/myapp: SELECT count(*) FROM users Log [info] (json format) [verbose]: Log level changed Verified multi-module configuration: DB Host: db.production.example.com Log Level: verbose
Index ¶
- Variables
- func GetConfigValue[T any](m *ConfigurableModule, name string) (T, error)
- func GetConfigValueWithFallback[T any](m *ConfigurableModule, name string, fallbackVal T) T
- func RunStarlarkTests(t *testing.T, moduleName string, moduleFactory func() starlet.ModuleLoader, ...)
- func RunTestScript(t *testing.T, script string, moduleName string, ...)
- func SetConfigDefault[T any](m *ConfigurableModule, name string, defaultValue T) error
- func SetConfigEnvVar[T any](m *ConfigurableModule, name string, envVar string) error
- func SetConfigGetter[T any](m *ConfigurableModule, name string, getter ConfigGetter[T]) error
- func SetConfigValue[T any](m *ConfigurableModule, name string, value T) error
- func SetTypedConfigOption[T any](m *ConfigurableModule, name string, option *ConfigOption[T]) error
- type ConfigGetter
- type ConfigOption
- func (o *ConfigOption[T]) FreezeHostOnlyEnv()
- func (o *ConfigOption[T]) GetInfo() map[string]interface{}
- func (o *ConfigOption[T]) GetName() string
- func (o *ConfigOption[T]) GetStarlarkValue() (starlark.Value, error)
- func (o *ConfigOption[T]) GetValue() (T, error)
- func (o *ConfigOption[T]) GetValueOrFallback(fallbackVal T) T
- func (o *ConfigOption[T]) HasDefault() bool
- func (o *ConfigOption[T]) HasEnvVar() bool
- func (o *ConfigOption[T]) HasGetter() bool
- func (o *ConfigOption[T]) HasValue() bool
- func (o *ConfigOption[T]) IsHostOnly() bool
- func (o *ConfigOption[T]) IsRequired() bool
- func (o *ConfigOption[T]) IsSecret() bool
- func (o *ConfigOption[T]) SetHostOnly(hostOnly bool) *ConfigOption[T]
- func (o *ConfigOption[T]) SetName(name string)
- func (o *ConfigOption[T]) SetRequired(required bool) *ConfigOption[T]
- func (o *ConfigOption[T]) SetSecret(secret bool) *ConfigOption[T]
- func (o *ConfigOption[T]) SetValue(value T) error
- func (o *ConfigOption[T]) SetValueFromStarlark(v starlark.Value) (err error)
- func (o *ConfigOption[T]) Validate() error
- func (o *ConfigOption[T]) WithDefault(defaultValue T) *ConfigOption[T]
- func (o *ConfigOption[T]) WithDescription(desc string) *ConfigOption[T]
- func (o *ConfigOption[T]) WithEnvVar(envVar string) *ConfigOption[T]
- func (o *ConfigOption[T]) WithGetter(getter ConfigGetter[T]) *ConfigOption[T]
- func (o *ConfigOption[T]) WithName(name string) *ConfigOption[T]
- func (o *ConfigOption[T]) WithValidator(validator ConfigValidator[T]) *ConfigOption[T]
- func (o *ConfigOption[T]) WithValue(value T) *ConfigOption[T]
- type ConfigOptionInterface
- type ConfigValidator
- type ConfigurableModule
- func (m *ConfigurableModule) Extend() *ConfigurableModuleExt
- func (m *ConfigurableModule) GetConfigOption(name string) (ConfigOptionInterface, error)
- func (m *ConfigurableModule) Initialize() error
- func (m *ConfigurableModule) ListConfigs() map[string]map[string]interface{}
- func (m *ConfigurableModule) LoadModule(moduleName string, additionalFuncs starlark.StringDict) starlet.ModuleLoader
- func (m *ConfigurableModule) SetConfigOption(name string, option ConfigOptionInterface) error
- type ConfigurableModuleExt
- func (e *ConfigurableModuleExt) GetBool(key string, fallbackVal ...bool) bool
- func (e *ConfigurableModuleExt) GetFloat(key string, fallbackVal ...float64) float64
- func (e *ConfigurableModuleExt) GetInt(key string, fallbackVal ...int) int
- func (e *ConfigurableModuleExt) GetString(key string, fallbackVal ...string) string
- func (e *ConfigurableModuleExt) GetUint(key string, fallbackVal ...uint) uint
- type ModuleOption
- func WithConfigDefault[T any](name string, defaultValue T) ModuleOption
- func WithConfigEnvVar[T any](name string, envVar string) ModuleOption
- func WithConfigGetter[T any](name string, getter ConfigGetter[T]) ModuleOption
- func WithConfigOption(name string, option ConfigOptionInterface) ModuleOption
- func WithConfigValue[T any](name string, value T) ModuleOption
- func WithTypedConfigOption[T any](name string, option *ConfigOption[T]) ModuleOption
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrConfigNotSet is the error when the configuration is not set. ErrConfigNotSet = errors.New("config not set") // ErrConfigRequired is the error when a required configuration is not set. ErrConfigRequired = errors.New("required config not set") // ErrConfigInvalidValue is the error when a configuration value is invalid. ErrConfigInvalidValue = errors.New("invalid config value") // ErrModuleAlreadyInitialized is the error when trying to modify a module after it's initialized. ErrModuleAlreadyInitialized = errors.New("module already initialized") // ErrSecretConfigNotRetrievable is the error when attempting to retrieve a secret configuration value. ErrSecretConfigNotRetrievable = errors.New("secret configuration is not retrievable") // ErrModuleNotInitialized is the error when trying to access a module that's not initialized. ErrModuleNotInitialized = errors.New("module not initialized") // ErrConfigOptionNotFound is the error when a configuration option is not found. ErrConfigOptionNotFound = errors.New("config option not found") // ErrConfigGetterPanic is the error when a config getter function panics. ErrConfigGetterPanic = errors.New("config getter panicked") )
Common errors returned by the configuration module.
Functions ¶
func GetConfigValue ¶ added in v0.0.2
func GetConfigValue[T any](m *ConfigurableModule, name string) (T, error)
GetConfigValue returns the value of a configuration option.
func GetConfigValueWithFallback ¶ added in v0.0.3
func GetConfigValueWithFallback[T any](m *ConfigurableModule, name string, fallbackVal T) T
GetConfigValueWithFallback retrieves a configuration value with a fallback. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned.
Example:
// Instead of:
val, err := GetConfigValue(m, key)
if err != nil {
val = fallbackVal
}
// You can use:
val := base.GetConfigValueWithFallback(m.cfgMod, key, fallbackVal)
For even more convenience, use the ConfigurableModuleExt methods via the Extend() function.
func RunStarlarkTests ¶ added in v0.0.5
func RunStarlarkTests(t *testing.T, moduleName string, moduleFactory func() starlet.ModuleLoader, extraModules []string, testDirPath string)
RunStarlarkTests is a helper function that runs Starlark test scripts from a specified test directory. Scripts with "test-" prefix should succeed, "panic-" prefix should fail.
Parameters:
- t: the testing instance
- moduleName: name of the module being tested
- moduleFactory: function that creates a fresh module loader for each test run
- extraModules: additional modules to include in the Starlark environment
- testDirPath: path to the directory containing test scripts (if empty, defaults to "../test/{moduleName}")
func RunTestScript ¶ added in v0.0.5
func RunTestScript(t *testing.T, script string, moduleName string, moduleFactory func() starlet.ModuleLoader, extraModuleLoaders map[string]starlet.ModuleLoader)
RunTestScript is a helper function that executes a Starlark script with the specified module. This reduces boilerplate code in test functions by centralizing the common setup logic.
Parameters:
- t: the testing instance
- script: the Starlark script content to execute
- moduleName: name of the module being tested
- moduleFactory: function that creates a fresh module loader
- extraModuleLoaders: optional map of additional module loaders to include
The function will automatically: 1. Create a new module instance using the provided factory 2. Set up a Starlet interpreter 3. Add the module and any extra modules to the interpreter 4. Execute the provided script 5. Handle any execution errors
func SetConfigDefault ¶ added in v0.0.3
func SetConfigDefault[T any](m *ConfigurableModule, name string, defaultValue T) error
SetConfigDefault sets a default value for the configuration. This has the lowest priority in the resolution order.
func SetConfigEnvVar ¶ added in v0.0.2
func SetConfigEnvVar[T any](m *ConfigurableModule, name string, envVar string) error
SetConfigEnvVar associates an environment variable with the configuration. This has the third highest priority in the resolution order.
func SetConfigGetter ¶ added in v0.0.2
func SetConfigGetter[T any](m *ConfigurableModule, name string, getter ConfigGetter[T]) error
SetConfigGetter registers a dynamic getter for the configuration. This has the second highest priority in the resolution order.
func SetConfigValue ¶ added in v0.0.2
func SetConfigValue[T any](m *ConfigurableModule, name string, value T) error
SetConfigValue sets the configuration value. This has the highest priority in the resolution order.
func SetTypedConfigOption ¶ added in v0.0.2
func SetTypedConfigOption[T any](m *ConfigurableModule, name string, option *ConfigOption[T]) error
SetTypedConfigOption registers a strongly-typed configuration option.
Types ¶
type ConfigGetter ¶
type ConfigGetter[T any] func() T
ConfigGetter is a function that returns a configuration value of type T.
type ConfigOption ¶ added in v0.0.2
type ConfigOption[T any] struct { // Configuration metadata Name string // Unique identifier for this configuration. Description string // Human-readable description of the configuration. EnvVar string // Environment variable name for overriding the configuration. // contains filtered or unexported fields }
ConfigOption represents a configuration option with a specific type. It supports default values, validation, dynamic getters, and environment variable overrides. The internal mutex protects all mutable fields.
Configuration values are resolved in the following priority order (from highest to lowest): 1. Immediate value (set via WithValue/SetValue) 2. Returned value from the getter function (set via WithGetter) 3. Environment variable value (set via WithEnvVar) 4. Default value (set via WithDefault or NewConfigOption)
Secret configuration values are accessible in Go code but will not be exposed to Starlark runtime or in the GetInfo() results to protect sensitive data.
func NewConfigOption ¶ added in v0.0.2
func NewConfigOption[T any](defaultValue T) *ConfigOption[T]
NewConfigOption creates a new configuration option with the given default value.
func NewNamedConfigOption ¶ added in v0.1.2
func NewNamedConfigOption[T any](module, name, description string, defaultValue T) *ConfigOption[T]
NewNamedConfigOption creates a configuration option with its name, description, and the conventional `<MODULE>_<NAME>` environment variable (both uppercased) filled in. It collapses the per-module `genConfigOption` helper every domain module hand-rolls — e.g. NewNamedConfigOption("yaml", "max_depth", "...", 64) derives the env var `YAML_MAX_DEPTH`.
func (*ConfigOption[T]) FreezeHostOnlyEnv ¶ added in v0.1.3
func (o *ConfigOption[T]) FreezeHostOnlyEnv()
FreezeHostOnlyEnv snapshots the environment-variable value of a host-only option so a later mutation of the process environment cannot change it. It is called once when the option is registered on a module — i.e. during Go construction, before any Starlark script runs — so the captured value is the one the host intended. It is a no-op for a non-host-only option (those stay dynamic, reading the live environment), for an option without an env var, and when already frozen. See SetHostOnly.
func (*ConfigOption[T]) GetInfo ¶ added in v0.0.2
func (o *ConfigOption[T]) GetInfo() map[string]interface{}
GetInfo returns information about the configuration option.
func (*ConfigOption[T]) GetName ¶ added in v0.0.2
func (o *ConfigOption[T]) GetName() string
GetName returns the name of the configuration option.
func (*ConfigOption[T]) GetStarlarkValue ¶ added in v0.0.2
func (o *ConfigOption[T]) GetStarlarkValue() (starlark.Value, error)
GetStarlarkValue returns the configuration value as a starlark value. This method provides the underlying mechanism to access configuration values in Starlark scripts. Note that while this method itself doesn't block secret values, secret configuration options are not exposed as get_* methods in Starlark runtime by the LoadModule method.
func (*ConfigOption[T]) GetValue ¶ added in v0.0.2
func (o *ConfigOption[T]) GetValue() (T, error)
GetValue returns the current value of the configuration option. The value is resolved according to the following priority order (from highest to lowest): 1. Immediate value (set via WithValue/SetValue) 2. Returned value from the getter function (set via WithGetter) 3. Environment variable value (set via WithEnvVar) 4. Default value (set via WithDefault or NewConfigOption)
Secret values can be accessed via this method in Go code.
func (*ConfigOption[T]) GetValueOrFallback ¶ added in v0.0.3
func (o *ConfigOption[T]) GetValueOrFallback(fallbackVal T) T
GetValueOrFallback returns the current value of the configuration option or the provided fallback value if retrieval fails. This is a convenience method to avoid having to handle errors when getting config values.
Example:
// Instead of:
val, err := option.GetValue()
if err != nil {
val = fallbackVal
}
// You can use:
val := option.GetValueOrFallback(fallbackVal)
func (*ConfigOption[T]) HasDefault ¶ added in v0.0.2
func (o *ConfigOption[T]) HasDefault() bool
HasDefault returns true if the default value is not the zero value.
func (*ConfigOption[T]) HasEnvVar ¶ added in v0.0.2
func (o *ConfigOption[T]) HasEnvVar() bool
HasEnvVar returns whether the configuration option has an environment variable specified.
func (*ConfigOption[T]) HasGetter ¶ added in v0.0.2
func (o *ConfigOption[T]) HasGetter() bool
HasGetter returns whether the configuration option has a getter.
func (*ConfigOption[T]) HasValue ¶ added in v0.0.2
func (o *ConfigOption[T]) HasValue() bool
HasValue returns whether the configuration option has a value set.
func (*ConfigOption[T]) IsHostOnly ¶ added in v0.1.2
func (o *ConfigOption[T]) IsHostOnly() bool
IsHostOnly returns whether the configuration option is host-only (not exposed to Starlark as a set_<name> builtin). See SetHostOnly.
func (*ConfigOption[T]) IsRequired ¶ added in v0.0.2
func (o *ConfigOption[T]) IsRequired() bool
IsRequired returns whether the configuration option is required.
func (*ConfigOption[T]) IsSecret ¶ added in v0.0.2
func (o *ConfigOption[T]) IsSecret() bool
IsSecret returns whether the configuration option is secret.
func (*ConfigOption[T]) SetHostOnly ¶ added in v0.1.2
func (o *ConfigOption[T]) SetHostOnly(hostOnly bool) *ConfigOption[T]
SetHostOnly sets whether the configuration option is host-only. A host-only option is NOT exposed to Starlark as a set_<name> builtin, so a script cannot change it; the host still configures it in Go and (unless it is also secret) a get_<name> builtin still lets a script read it. This is for safety- or resource-sensitive limits — e.g. a maximum input size — that a module must be able to enforce against untrusted scripts, which a script-settable option cannot do (it could just raise or zero the limit). Defaults to false, so existing options remain script-settable exactly as before.
A host-only option's environment-variable value is also snapshotted when the option is registered on a module (see FreezeHostOnlyEnv), so a script that can mutate the process environment at runtime (e.g. via a runtime.setenv builtin) cannot re-widen the limit through its env var either.
func (*ConfigOption[T]) SetName ¶ added in v0.0.2
func (o *ConfigOption[T]) SetName(name string)
SetName sets the name of the configuration option.
func (*ConfigOption[T]) SetRequired ¶ added in v0.0.2
func (o *ConfigOption[T]) SetRequired(required bool) *ConfigOption[T]
SetRequired sets whether the configuration option is required.
func (*ConfigOption[T]) SetSecret ¶ added in v0.0.2
func (o *ConfigOption[T]) SetSecret(secret bool) *ConfigOption[T]
SetSecret sets whether the configuration option is secret.
func (*ConfigOption[T]) SetValue ¶ added in v0.0.2
func (o *ConfigOption[T]) SetValue(value T) error
SetValue sets the value of the configuration option. It validates the value if a validator is set.
func (*ConfigOption[T]) SetValueFromStarlark ¶ added in v0.0.2
func (o *ConfigOption[T]) SetValueFromStarlark(v starlark.Value) (err error)
SetValueFromStarlark sets the configuration option from a Starlark value.
It never lets a reflection/conversion panic escape to the host: a recover guard (mirroring resolveValue) turns any panic into an error, and None/nil input is rejected up front (dataconv.Unmarshal(None) yields a nil interface, whose reflect.TypeOf is a nil Type that would panic on Kind()).
func (*ConfigOption[T]) Validate ¶ added in v0.0.2
func (o *ConfigOption[T]) Validate() error
Validate validates the current value if a validator is set. This method ONLY validates the explicitly set value (via SetValue or WithValue), and does NOT validate values returned from a getter function or environment, or default values. Returns nil if no validator is set, no value has been set, or the validation passes.
func (*ConfigOption[T]) WithDefault ¶ added in v0.0.3
func (o *ConfigOption[T]) WithDefault(defaultValue T) *ConfigOption[T]
WithDefault sets the default value of the configuration option. This has the lowest priority in the resolution order.
func (*ConfigOption[T]) WithDescription ¶ added in v0.0.2
func (o *ConfigOption[T]) WithDescription(desc string) *ConfigOption[T]
WithDescription adds a description to the configuration option.
func (*ConfigOption[T]) WithEnvVar ¶ added in v0.0.2
func (o *ConfigOption[T]) WithEnvVar(envVar string) *ConfigOption[T]
WithEnvVar specifies an environment variable name to check for this configuration. This has the third highest priority in the resolution order.
func (*ConfigOption[T]) WithGetter ¶ added in v0.0.2
func (o *ConfigOption[T]) WithGetter(getter ConfigGetter[T]) *ConfigOption[T]
WithGetter adds a custom getter to the configuration option. This has the second highest priority in the resolution order.
func (*ConfigOption[T]) WithName ¶ added in v0.0.2
func (o *ConfigOption[T]) WithName(name string) *ConfigOption[T]
WithName sets the name of the configuration option.
func (*ConfigOption[T]) WithValidator ¶ added in v0.0.2
func (o *ConfigOption[T]) WithValidator(validator ConfigValidator[T]) *ConfigOption[T]
WithValidator adds a validator to the configuration option.
func (*ConfigOption[T]) WithValue ¶ added in v0.0.2
func (o *ConfigOption[T]) WithValue(value T) *ConfigOption[T]
WithValue sets the value of the configuration option. This is useful for chain calls when building a configuration option. Unlike SetValue, this method ignores any validators since it's part of a builder chain. Validation will occur during module initialization when Initialize() is called. This has the highest priority in the resolution order.
type ConfigOptionInterface ¶ added in v0.0.2
type ConfigOptionInterface interface {
// Core methods
GetName() string
SetName(name string)
IsRequired() bool
IsSecret() bool
IsHostOnly() bool
FreezeHostOnlyEnv()
HasValue() bool
HasGetter() bool
HasDefault() bool
HasEnvVar() bool
Validate() error
// Starlark integration
SetValueFromStarlark(v starlark.Value) error
GetStarlarkValue() (starlark.Value, error)
// Go inspection
GetInfo() map[string]interface{}
}
ConfigOptionInterface defines the common interface for configuration options. It is used by ConfigurableModule to manage configuration settings.
type ConfigValidator ¶ added in v0.0.2
ConfigValidator is a function that validates a configuration value of type T.
type ConfigurableModule ¶
type ConfigurableModule struct {
// contains filtered or unexported fields
}
ConfigurableModule provides a generic module that can be extended with different configurations. Once initialized, the module becomes immutable.
Configuration values are resolved in the following priority order (from highest to lowest): 1. Immediate value (set via WithValue/SetValue) 2. Returned value from the getter function (set via WithGetter) 3. Environment variable value (set via WithEnvVar) 4. Default value (set via WithDefault or NewConfigOption)
func NewConfigurableModule ¶
func NewConfigurableModule() *ConfigurableModule
NewConfigurableModule returns a new instance of ConfigurableModule.
func NewConfigurableModuleWithConfigOptions ¶ added in v0.0.3
func NewConfigurableModuleWithConfigOptions(options ...ConfigOptionInterface) (*ConfigurableModule, error)
NewConfigurableModuleWithConfigOptions returns a new instance of ConfigurableModule with the provided ConfigOptions added. Unlike NewConfigurableModuleWithOptions which takes functional ModuleOptions, this function directly accepts the actual ConfigOption instances. This is a more convenient way to create a module when you have ConfigOption instances directly.
The name of each option is inferred from its Name field if set, otherwise a generated name will be used following the pattern "option_1", "option_2", etc.
Example:
// Create config options
strOpt := base.NewConfigOption("default_str").WithName("string_option")
intOpt := base.NewConfigOption(42).WithName("int_option")
// Create module with options
module, err := base.NewConfigurableModuleWithConfigOptions(strOpt, intOpt)
func NewConfigurableModuleWithOptions ¶ added in v0.0.2
func NewConfigurableModuleWithOptions(options ...ModuleOption) (*ConfigurableModule, error)
NewConfigurableModuleWithOptions returns a new instance of ConfigurableModule with the provided options applied.
func (*ConfigurableModule) Extend ¶ added in v0.0.3
func (m *ConfigurableModule) Extend() *ConfigurableModuleExt
Extend returns an extension wrapper for ConfigurableModule that provides additional convenience methods. This helps reduce boilerplate code when working with configuration values that need fallback handling.
func (*ConfigurableModule) GetConfigOption ¶ added in v0.0.2
func (m *ConfigurableModule) GetConfigOption(name string) (ConfigOptionInterface, error)
GetConfigOption retrieves a configuration option by name.
func (*ConfigurableModule) Initialize ¶ added in v0.0.2
func (m *ConfigurableModule) Initialize() error
Initialize finalizes the module configuration and prevents further modifications.
func (*ConfigurableModule) ListConfigs ¶ added in v0.0.2
func (m *ConfigurableModule) ListConfigs() map[string]map[string]interface{}
ListConfigs returns a map with configuration details for each option.
func (*ConfigurableModule) LoadModule ¶
func (m *ConfigurableModule) LoadModule(moduleName string, additionalFuncs starlark.StringDict) starlet.ModuleLoader
LoadModule returns a Starlark module loader with registered built-in functions. Initialization (and its validation) is deferred into the returned loader, so a configuration error surfaces as the loader's error return rather than a panic that would crash the host (PKG-03).
func (*ConfigurableModule) SetConfigOption ¶ added in v0.0.2
func (m *ConfigurableModule) SetConfigOption(name string, option ConfigOptionInterface) error
SetConfigOption registers a configuration option for the module.
type ConfigurableModuleExt ¶ added in v0.0.3
type ConfigurableModuleExt struct {
*ConfigurableModule
}
ConfigurableModuleExt provides extension methods for ConfigurableModule. It offers convenient access to configuration values with built-in fallback handling, reducing boilerplate code in modules that use ConfigurableModule.
func (*ConfigurableModuleExt) GetBool ¶ added in v0.0.3
func (e *ConfigurableModuleExt) GetBool(key string, fallbackVal ...bool) bool
GetBool retrieves a bool configuration value with an optional fallback value. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned. This provides the same convenience as GetString but for boolean values.
func (*ConfigurableModuleExt) GetFloat ¶ added in v0.0.3
func (e *ConfigurableModuleExt) GetFloat(key string, fallbackVal ...float64) float64
GetFloat retrieves a float64 configuration value with an optional fallback value. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned. This provides the same convenience as GetString but for float64 values.
func (*ConfigurableModuleExt) GetInt ¶ added in v0.0.3
func (e *ConfigurableModuleExt) GetInt(key string, fallbackVal ...int) int
GetInt retrieves an int configuration value with an optional fallback value. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned. This provides the same convenience as GetString but for int values.
func (*ConfigurableModuleExt) GetString ¶ added in v0.0.3
func (e *ConfigurableModuleExt) GetString(key string, fallbackVal ...string) string
GetString retrieves a string configuration value with an optional fallback value. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned. This is a convenience method replacing the common pattern of checking for errors when retrieving config values.
func (*ConfigurableModuleExt) GetUint ¶ added in v0.0.3
func (e *ConfigurableModuleExt) GetUint(key string, fallbackVal ...uint) uint
GetUint retrieves an uint configuration value with an optional fallback value. If the configuration doesn't exist or there's an error retrieving it, the fallback value is returned. This provides the same convenience as GetString but for unsigned integer values.
type ModuleOption ¶ added in v0.0.2
type ModuleOption func(*ConfigurableModule) error
ModuleOption applies a configuration to the module.
func WithConfigDefault ¶ added in v0.0.3
func WithConfigDefault[T any](name string, defaultValue T) ModuleOption
WithConfigDefault sets a default value for the configuration. This has the lowest priority in the resolution order.
func WithConfigEnvVar ¶ added in v0.0.2
func WithConfigEnvVar[T any](name string, envVar string) ModuleOption
WithConfigEnvVar associates an environment variable with the configuration. This has the third highest priority in the resolution order.
func WithConfigGetter ¶ added in v0.0.2
func WithConfigGetter[T any](name string, getter ConfigGetter[T]) ModuleOption
WithConfigGetter registers a dynamic getter for the configuration. This has the second highest priority in the resolution order.
func WithConfigOption ¶ added in v0.0.2
func WithConfigOption(name string, option ConfigOptionInterface) ModuleOption
WithConfigOption registers a configuration option for the module.
func WithConfigValue ¶ added in v0.0.2
func WithConfigValue[T any](name string, value T) ModuleOption
WithConfigValue sets a configuration value directly. This has the highest priority in the resolution order.
func WithTypedConfigOption ¶ added in v0.0.2
func WithTypedConfigOption[T any](name string, option *ConfigOption[T]) ModuleOption
WithTypedConfigOption registers a strongly-typed configuration option for the module.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package util provides small, hardened safety primitives shared across the starpkg modules: bounded I/O, constant-time secret comparison, float-safe duration parsing, and panic recovery.
|
Package util provides small, hardened safety primitives shared across the starpkg modules: bounded I/O, constant-time secret comparison, float-safe duration parsing, and panic recovery. |