Moonshark/core/config/Config.go
2025-04-04 10:49:16 -05:00

277 lines
5.5 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
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) {
// Create Lua state
state := luajit.New(true)
if state == nil {
return nil, errors.New("failed to create Lua state")
}
defer state.Close()
defer state.Cleanup()
// Create config with default values
config := New()
// Execute the config file
if err := state.DoFile(filePath); err != nil {
return nil, fmt.Errorf("failed to load config file: %w", err)
}
// Store values directly to the config
config.values = make(map[string]any)
// Extract all globals individually
globalKeys := []string{
"debug", "log_level", "port", "routes_dir", "static_dir",
"override_dir", "pool_size", "http_logging_enabled",
"watchers", "lib_dirs",
}
for _, key := range globalKeys {
state.GetGlobal(key)
if !state.IsNil(-1) {
value, err := state.ToValue(-1)
if err == nil {
config.values[key] = value
}
}
state.Pop(1)
}
// Apply configuration values
applyConfig(config, config.values)
return config, nil
}
// applyConfig applies configuration values from the globals map
func applyConfig(config *Config, globals map[string]any) {
// Server settings
if v, ok := globals["debug"].(bool); ok {
config.Debug = v
}
if v, ok := globals["log_level"].(string); ok {
config.LogLevel = v
}
if v, ok := globals["port"].(float64); ok {
config.Port = int(v)
}
// Directory paths
if v, ok := globals["routes_dir"].(string); ok {
config.RoutesDir = v
}
if v, ok := globals["static_dir"].(string); ok {
config.StaticDir = v
}
if v, ok := globals["override_dir"].(string); ok {
config.OverrideDir = v
}
// Performance settings
if v, ok := globals["pool_size"].(float64); ok {
config.PoolSize = int(v)
}
// Feature flags
if v, ok := globals["http_logging_enabled"].(bool); ok {
config.HTTPLoggingEnabled = v
}
// Handle lib_dirs array more efficiently
if libDirs, ok := globals["lib_dirs"]; ok {
if dirs := extractStringArray(libDirs); len(dirs) > 0 {
config.LibDirs = dirs
}
}
// Handle watchers table
if watchersVal, ok := globals["watchers"]; ok {
if watchers, ok := watchersVal.(map[string]any); ok {
if v, ok := watchers["routes"].(bool); ok {
config.Watchers.Routes = v
}
if v, ok := watchers["modules"].(bool); ok {
config.Watchers.Modules = v
}
}
}
}
// extractStringArray extracts a string array from a Lua table
func extractStringArray(value any) []string {
// Direct array case
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
}
// Map with numeric keys case
if tableMap, ok := value.(map[string]any); ok {
result := make([]string, 0, len(tableMap))
for _, v := range tableMap {
if str, ok := v.(string); ok {
result = append(result, str)
}
}
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
}