Moonshark/core/config/Config.go
2025-04-03 12:59:12 -05:00

313 lines
6.2 KiB
Go

package config
import (
"errors"
"fmt"
"runtime"
"strconv"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
// Config represents a configuration loaded from a Lua file
type Config struct {
// Server settings
LogLevel string
Port int
Debug bool
// Directory paths
RoutesDir string
StaticDir string
OverrideDir string
LibDirs []string
// Performance settings
PoolSize int
// Feature flags
HTTPLoggingEnabled bool
Watchers struct {
Routes bool
Static bool
Modules bool
}
// Raw values map for custom values
values map[string]any
}
// New creates a new configuration with default values
func New() *Config {
return &Config{
// Server defaults
LogLevel: "info",
Port: 3117,
Debug: false,
// Directory defaults
RoutesDir: "./routes",
StaticDir: "./static",
OverrideDir: "./override",
LibDirs: []string{"./libs"},
// Performance defaults - respect GOMAXPROCS
PoolSize: runtime.GOMAXPROCS(0),
// Feature flag defaults
HTTPLoggingEnabled: true,
// Initialize values map
values: make(map[string]any),
}
}
// Load loads configuration from a Lua file
func Load(filePath string) (*Config, error) {
// For simple configs, use standard libraries to ensure proper loading
state := luajit.New(true)
if state == nil {
return nil, errors.New("failed to create Lua state")
}
defer state.Close()
// Create config with default values
config := New()
// Execute the config file
if err := state.DoFile(filePath); err != nil {
return nil, fmt.Errorf("%w", err)
}
// Extract configuration values
extractConfig(state, config)
return config, nil
}
// extractConfig extracts configuration values directly by name
func extractConfig(state *luajit.State, config *Config) {
// Process known config values directly by key
if value := extractValue(state, "debug"); value != nil {
if boolVal, ok := value.(bool); ok {
config.Debug = boolVal
}
}
if value := extractValue(state, "log_level"); value != nil {
if strVal, ok := value.(string); ok {
config.LogLevel = strVal
}
}
if value := extractValue(state, "port"); value != nil {
if numVal, ok := value.(float64); ok {
config.Port = int(numVal)
}
}
if value := extractValue(state, "routes_dir"); value != nil {
if strVal, ok := value.(string); ok {
config.RoutesDir = strVal
}
}
if value := extractValue(state, "static_dir"); value != nil {
if strVal, ok := value.(string); ok {
config.StaticDir = strVal
}
}
if value := extractValue(state, "override_dir"); value != nil {
if strVal, ok := value.(string); ok {
config.OverrideDir = strVal
}
}
if value := extractValue(state, "pool_size"); value != nil {
if numVal, ok := value.(float64); ok {
config.PoolSize = int(numVal)
}
}
if value := extractValue(state, "http_logging_enabled"); value != nil {
if boolVal, ok := value.(bool); ok {
config.HTTPLoggingEnabled = boolVal
}
}
// Handle watchers table
if value := extractValue(state, "watchers"); value != nil {
if table, ok := value.(map[string]any); ok {
if routes, ok := table["routes"].(bool); ok {
config.Watchers.Routes = routes
}
if static, ok := table["static"].(bool); ok {
config.Watchers.Static = static
}
if modules, ok := table["modules"].(bool); ok {
config.Watchers.Modules = modules
}
}
}
// Handle lib_dirs array
if value := extractValue(state, "lib_dirs"); value != nil {
if dirs := extractStringArray(value); len(dirs) > 0 {
config.LibDirs = dirs
}
}
}
// extractValue extracts a specific global value from the Lua state
func extractValue(state *luajit.State, key string) any {
state.GetGlobal(key)
defer state.Pop(1)
if state.IsNil(-1) {
return nil
}
value, err := state.ToValue(-1)
if err != nil {
return nil
}
return value
}
// extractStringArray extracts a string array from various possible formats
func extractStringArray(value any) []string {
// Check if it's a direct array
if arr, ok := value.([]any); ok {
result := make([]string, 0, len(arr))
for _, v := range arr {
if str, ok := v.(string); ok {
result = append(result, str)
}
}
return result
}
// Check if it's in special array format (map with empty key)
valueMap, ok := value.(map[string]any)
if !ok {
return nil
}
arr, ok := valueMap[""]
if !ok {
return nil
}
// Handle different array types
switch arr := arr.(type) {
case []string:
return arr
case []any:
result := make([]string, 0, len(arr))
for _, v := range arr {
if str, ok := v.(string); ok {
result = append(result, str)
}
}
return result
case []float64:
result := make([]string, 0, len(arr))
for _, v := range arr {
result = append(result, fmt.Sprintf("%g", v))
}
return result
}
return nil
}
// GetCustomValue returns any custom configuration value by key
func (c *Config) GetCustomValue(key string) any {
return c.values[key]
}
// GetCustomString returns a custom string configuration value
func (c *Config) GetCustomString(key string, defaultValue string) string {
value := c.values[key]
if value == nil {
return defaultValue
}
// Convert to string
switch v := value.(type) {
case string:
return v
case float64:
return fmt.Sprintf("%g", v)
case int:
return strconv.Itoa(v)
case bool:
if v {
return "true"
}
return "false"
default:
return defaultValue
}
}
// GetCustomInt returns a custom integer configuration value
func (c *Config) GetCustomInt(key string, defaultValue int) int {
value := c.values[key]
if value == nil {
return defaultValue
}
// Convert to int
switch v := value.(type) {
case int:
return v
case float64:
return int(v)
case string:
if i, err := strconv.Atoi(v); err == nil {
return i
}
case bool:
if v {
return 1
}
return 0
default:
return defaultValue
}
return defaultValue
}
// GetCustomBool returns a custom boolean configuration value
func (c *Config) GetCustomBool(key string, defaultValue bool) bool {
value := c.values[key]
if value == nil {
return defaultValue
}
// Convert to bool
switch v := value.(type) {
case bool:
return v
case string:
switch v {
case "true", "yes", "1":
return true
case "false", "no", "0", "":
return false
}
case int:
return v != 0
case float64:
return v != 0
default:
return defaultValue
}
return defaultValue
}