diff --git a/core/config/Config.go b/core/config/Config.go index 8059130..8901b84 100644 --- a/core/config/Config.go +++ b/core/config/Config.go @@ -22,14 +22,13 @@ type Config struct { LibDirs []string // Performance settings - BufferSize int - PoolSize int // Number of Lua states in the pool + PoolSize int // Number of Lua states in the pool // Feature flags HTTPLoggingEnabled bool Watchers map[string]bool - // Raw values map for backward compatibility and custom values + // Raw values map for all values including custom ones values map[string]any } @@ -65,8 +64,8 @@ func New() *Config { // Load loads configuration from a Lua file func Load(filePath string) (*Config, error) { - // Create a new Lua state - state := luajit.New() + // Create a new Lua state without standard libraries + state := luajit.New(false) if state == nil { return nil, errors.New("failed to create Lua state") } @@ -80,7 +79,7 @@ func Load(filePath string) (*Config, error) { return nil, fmt.Errorf("failed to load config file: %w", err) } - // Extract values from the Lua state + // Extract all globals from the Lua state if err := extractGlobals(state, config); err != nil { return nil, err } @@ -90,85 +89,38 @@ func Load(filePath string) (*Config, error) { // extractGlobals extracts global variables from the Lua state func extractGlobals(state *luajit.State, config *Config) error { - // Get the globals table (_G) + // Get the globals table state.GetGlobal("_G") if !state.IsTable(-1) { state.Pop(1) return errors.New("failed to get globals table") } - // Pre-populate with standard globals for reference checking - stdGlobals := map[string]bool{ - "_G": true, "_VERSION": true, "assert": true, "collectgarbage": true, - "coroutine": true, "debug": true, "dofile": true, "error": true, - "getmetatable": true, "io": true, "ipairs": true, "load": true, - "loadfile": true, "loadstring": true, "math": true, "next": true, - "os": true, "package": true, "pairs": true, "pcall": true, - "print": true, "rawequal": true, "rawget": true, "rawset": true, - "require": true, "select": true, "setmetatable": true, "string": true, - "table": true, "tonumber": true, "tostring": true, "type": true, - "unpack": true, "xpcall": true, - // LuaJIT specific globals - "jit": true, "bit": true, "ffi": true, "bit32": true, - } - - // First, let's get the original globals to compare with user globals - originalGlobals := make(map[string]bool) - - // Execute empty Lua state to get standard globals - emptyState := luajit.New() - if emptyState != nil { - defer emptyState.Close() - - emptyState.GetGlobal("_G") - emptyState.PushNil() // Start iteration - for emptyState.Next(-2) { - if emptyState.IsString(-2) { - key := emptyState.ToString(-2) - originalGlobals[key] = true - } - emptyState.Pop(1) // Pop value, leave key for next iteration - } - emptyState.Pop(1) // Pop _G - } - // Iterate through the globals table state.PushNil() // Start iteration for state.Next(-2) { // Stack now has key at -2 and value at -1 - // Get key as string + // Skip non-string keys if !state.IsString(-2) { state.Pop(1) // Pop value, leave key for next iteration continue } + // Get key and value type key := state.ToString(-2) - - // Skip standard Lua globals, but only if they're not overridden by user - // (standard globals will be functions or tables, user values usually aren't) valueType := state.GetType(-1) - // Skip functions, userdata, and threads regardless of origin - if valueType == luajit.TypeFunction || valueType == luajit.TypeUserData || valueType == luajit.TypeThread { + // Skip functions, userdata, and threads - we don't need these + if valueType == luajit.TypeFunction || + valueType == luajit.TypeUserData || + valueType == luajit.TypeThread { state.Pop(1) continue } - // For known Lua globals, we need to see if they're the original or user-defined - if stdGlobals[key] { - // For simple value types, assume user-defined - if valueType == luajit.TypeBoolean || valueType == luajit.TypeNumber || valueType == luajit.TypeString { - // These are probably user values with standard names - } else if originalGlobals[key] { - // If it's in the original globals and not a simple type, skip it - state.Pop(1) - continue - } - } - - // Process based on key and type - processConfigValue(state, config, key, valueType) + // Process the value + processConfigValue(state, config, key) } // Pop the globals table @@ -178,30 +130,10 @@ func extractGlobals(state *luajit.State, config *Config) error { } // processConfigValue processes a specific config value from Lua -func processConfigValue(state *luajit.State, config *Config, key string, valueType luajit.LuaType) { - // Store in the values map first (for backward compatibility) - var value any - - // Extract the value based on its type - switch valueType { - case luajit.TypeBoolean: - value = state.ToBoolean(-1) - case luajit.TypeNumber: - value = state.ToNumber(-1) - case luajit.TypeString: - value = state.ToString(-1) - case luajit.TypeTable: - // For tables, use the existing conversion logic - if table, err := state.ToTable(-1); err == nil { - value = table - - // Special case for watchers table - if key == "watchers" { - processWatchersTable(config, table) - } - } - default: - // Skip unsupported types +func processConfigValue(state *luajit.State, config *Config, key string) { + // Get the value as its natural type + value, err := state.ToValue(-1) + if err != nil { state.Pop(1) return } @@ -209,112 +141,101 @@ func processConfigValue(state *luajit.State, config *Config, key string, valueTy // Store in the values map config.values[key] = value - // Now set specific struct fields based on key + // Process special cases and config fields switch key { case "log_level": - if strVal, ok := value.(string); ok { + if strVal, ok := luajit.ConvertValue[string](value); ok { config.LogLevel = strVal } case "port": - if numVal, ok := value.(float64); ok { - config.Port = int(numVal) + if intVal, ok := luajit.ConvertValue[int](value); ok { + config.Port = intVal } case "debug": - if boolVal, ok := value.(bool); ok { + if boolVal, ok := luajit.ConvertValue[bool](value); ok { config.Debug = boolVal } case "routes_dir": - if strVal, ok := value.(string); ok { + if strVal, ok := luajit.ConvertValue[string](value); ok { config.RoutesDir = strVal } case "static_dir": - if strVal, ok := value.(string); ok { + if strVal, ok := luajit.ConvertValue[string](value); ok { config.StaticDir = strVal } case "override_dir": - if strVal, ok := value.(string); ok { + if strVal, ok := luajit.ConvertValue[string](value); ok { config.OverrideDir = strVal } case "pool_size": - if numVal, ok := value.(float64); ok { - config.PoolSize = int(numVal) + if intVal, ok := luajit.ConvertValue[int](value); ok { + config.PoolSize = intVal } case "http_logging_enabled": - if boolVal, ok := value.(bool); ok { + if boolVal, ok := luajit.ConvertValue[bool](value); ok { config.HTTPLoggingEnabled = boolVal } + case "watchers": + if table, ok := value.(map[string]any); ok { + for k, v := range table { + if boolVal, ok := luajit.ConvertValue[bool](v); ok { + config.Watchers[k] = boolVal + } + } + } case "lib_dirs": - // Handle lib_dirs array - processLibDirs(config, value) + if dirs := extractStringArray(value); len(dirs) > 0 { + config.LibDirs = dirs + } } state.Pop(1) // Pop value, leave key for next iteration } -// processWatchersTable processes the watchers table configuration -func processWatchersTable(config *Config, watchersTable map[string]any) { - for key, value := range watchersTable { - if boolVal, ok := value.(bool); ok { - config.Watchers[key] = boolVal - } - } -} - -// processLibDirs processes the lib_dirs array configuration -func processLibDirs(config *Config, value any) { +// 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 { + if str, ok := luajit.ConvertValue[string](v); ok { result = append(result, str) } } - if len(result) > 0 { - config.LibDirs = result - } - return + return result } - // Check if it's in our special array format (map with empty key) + // Check if it's in special array format (map with empty key) valueMap, ok := value.(map[string]any) if !ok { - return + return nil } arr, ok := valueMap[""] if !ok { - return + return nil } - // Handle array format - if strArray := extractStringArray(arr); len(strArray) > 0 { - config.LibDirs = strArray - } -} - -// extractStringArray extracts a string array from various possible formats -func extractStringArray(arr any) []string { - // Check different possible array formats + // 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 { + if str, ok := luajit.ConvertValue[string](v); ok { result = append(result, str) } } return result case []float64: - // Unlikely but handle numeric arrays too result := make([]string, 0, len(arr)) for _, v := range arr { result = append(result, fmt.Sprintf("%g", v)) } return result } + return nil } @@ -325,7 +246,7 @@ func (c *Config) Get(key string) any { // GetString returns a string configuration value func (c *Config) GetString(key string, defaultValue string) string { - // Check for specific struct fields first + // Check for specific struct fields first for better performance switch key { case "log_level": return c.LogLevel @@ -343,22 +264,20 @@ func (c *Config) GetString(key string, defaultValue string) string { return defaultValue } - str, ok := value.(string) + result, ok := luajit.ConvertValue[string](value) if !ok { return defaultValue } - return str + return result } // GetInt returns an integer configuration value func (c *Config) GetInt(key string, defaultValue int) int { - // Check for specific struct fields first + // Check for specific struct fields first for better performance switch key { case "port": return c.Port - case "buffer_size": - return c.BufferSize case "pool_size": return c.PoolSize } @@ -369,15 +288,12 @@ func (c *Config) GetInt(key string, defaultValue int) int { return defaultValue } - // Handle both int and float64 (which is what Lua numbers become in Go) - switch v := value.(type) { - case int: - return v - case float64: - return int(v) - default: + result, ok := luajit.ConvertValue[int](value) + if !ok { return defaultValue } + + return result } // GetFloat returns a float configuration value @@ -387,20 +303,17 @@ func (c *Config) GetFloat(key string, defaultValue float64) float64 { return defaultValue } - // Handle both float64 and int - switch v := value.(type) { - case float64: - return v - case int: - return float64(v) - default: + result, ok := luajit.ConvertValue[float64](value) + if !ok { return defaultValue } + + return result } // GetBool returns a boolean configuration value func (c *Config) GetBool(key string, defaultValue bool) bool { - // Check for specific struct fields first + // Check for specific struct fields first for better performance switch key { case "debug": return c.Debug @@ -409,12 +322,13 @@ func (c *Config) GetBool(key string, defaultValue bool) bool { } // Special case for watcher settings - if key == "watchers.routes" { - return c.Watchers["routes"] - } else if key == "watchers.static" { - return c.Watchers["static"] - } else if key == "watchers.modules" { - return c.Watchers["modules"] + if len(key) > 9 && key[:9] == "watchers." { + watcherKey := key[9:] + val, ok := c.Watchers[watcherKey] + if ok { + return val + } + return defaultValue } // Fall back to values map for other keys @@ -423,19 +337,19 @@ func (c *Config) GetBool(key string, defaultValue bool) bool { return defaultValue } - boolValue, ok := value.(bool) + result, ok := luajit.ConvertValue[bool](value) if !ok { return defaultValue } - return boolValue + return result } // GetMap returns a map configuration value func (c *Config) GetMap(key string) map[string]any { // Special case for watchers if key == "watchers" { - result := make(map[string]any) + result := make(map[string]any, len(c.Watchers)) for k, v := range c.Watchers { result[k] = v } @@ -476,7 +390,7 @@ func (c *Config) GetArray(key string) []any { return arr } - // Arrays in Lua might also be represented as maps with an empty string key + // Arrays in Lua might be represented as maps with an empty string key valueMap, ok := value.(map[string]any) if !ok { return nil @@ -507,51 +421,6 @@ func (c *Config) GetArray(key string) []any { return anyArr } -// GetIntArray returns an array of integers from a Lua array -func (c *Config) GetIntArray(key string) []int { - value := c.Get(key) - if value == nil { - return nil - } - - // Direct array case - if arr, ok := value.([]any); ok { - result := make([]int, 0, len(arr)) - for _, v := range arr { - if num, ok := v.(float64); ok { - result = append(result, int(num)) - } - } - return result - } - - // Arrays in Lua might also be represented as maps with an empty string key - valueMap, ok := value.(map[string]any) - if !ok { - return nil - } - - // The array data is stored with an empty key - arr, ok := valueMap[""] - if !ok { - return nil - } - - // For numeric arrays, LuaJIT returns []float64 - floatArr, ok := arr.([]float64) - if !ok { - return nil - } - - // Convert to int slice - result := make([]int, len(floatArr)) - for i, v := range floatArr { - result[i] = int(v) - } - - return result -} - // GetStringArray returns an array of strings from a Lua array func (c *Config) GetStringArray(key string) []string { // Special case for lib_dirs @@ -566,7 +435,7 @@ func (c *Config) GetStringArray(key string) []string { result := make([]string, 0, len(arr)) for _, v := range arr { - if str, ok := v.(string); ok { + if str, ok := luajit.ConvertValue[string](v); ok { result = append(result, str) } } @@ -575,52 +444,6 @@ func (c *Config) GetStringArray(key string) []string { } // Values returns all configuration values -// Note: The returned map should not be modified func (c *Config) Values() map[string]any { return c.values } - -// Set sets a configuration value -func (c *Config) Set(key string, value any) { - c.values[key] = value - - // Also update the struct field if applicable - switch key { - case "log_level": - if strVal, ok := value.(string); ok { - c.LogLevel = strVal - } - case "port": - if numVal, ok := value.(float64); ok { - c.Port = int(numVal) - } else if intVal, ok := value.(int); ok { - c.Port = intVal - } - case "debug": - if boolVal, ok := value.(bool); ok { - c.Debug = boolVal - } - case "routes_dir": - if strVal, ok := value.(string); ok { - c.RoutesDir = strVal - } - case "static_dir": - if strVal, ok := value.(string); ok { - c.StaticDir = strVal - } - case "override_dir": - if strVal, ok := value.(string); ok { - c.OverrideDir = strVal - } - case "pool_size": - if numVal, ok := value.(float64); ok { - c.PoolSize = int(numVal) - } else if intVal, ok := value.(int); ok { - c.PoolSize = intVal - } - case "http_logging_enabled": - if boolVal, ok := value.(bool); ok { - c.HTTPLoggingEnabled = boolVal - } - } -} diff --git a/luajit b/luajit index 0756cab..a2b4b1c 160000 --- a/luajit +++ b/luajit @@ -1 +1 @@ -Subproject commit 0756cabcaaf1e33f2b8eb535e5a24e448c2501a9 +Subproject commit a2b4b1c9272f849d9c1c913366f822e0be904ba2