Moonshark/core/config/Config.go

291 lines
5.9 KiB
Go

package config
import (
"errors"
"fmt"
"runtime"
"strconv"
"strings"
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
)
// Config represents a configuration loaded from a Lua file
type Config struct {
// Server settings
Server struct {
Port int
Debug bool
LogLevel string
HTTPLogging bool
}
// Runner settings
Runner struct {
PoolSize int
}
// Directory paths
Dirs struct {
Routes string
Static string
FS string
Data string
Override string
Libs []string
}
// Raw values map for custom values
values map[string]any
}
// New creates a new configuration with default values
func New() *Config {
config := &Config{
// Initialize values map
values: make(map[string]any),
}
// Server defaults
config.Server.Port = 3117
config.Server.Debug = false
config.Server.LogLevel = "info"
config.Server.HTTPLogging = false
// Runner defaults
config.Runner.PoolSize = runtime.GOMAXPROCS(0)
// Dirs defaults
config.Dirs.Routes = "./routes"
config.Dirs.Static = "./static"
config.Dirs.FS = "./fs"
config.Dirs.Data = "./data"
config.Dirs.Override = "./override"
config.Dirs.Libs = []string{"./libs"}
return config
}
// 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 top-level tables
tables := []string{"server", "runner", "dirs"}
for _, table := range tables {
state.GetGlobal(table)
if state.IsTable(-1) {
tableMap, err := state.ToTable(-1)
if err == nil {
config.values[table] = tableMap
}
}
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, values map[string]any) {
// Apply server settings
if serverTable, ok := values["server"].(map[string]any); ok {
if v, ok := serverTable["port"].(float64); ok {
config.Server.Port = int(v)
}
if v, ok := serverTable["debug"].(bool); ok {
config.Server.Debug = v
}
if v, ok := serverTable["log_level"].(string); ok {
config.Server.LogLevel = v
}
if v, ok := serverTable["http_logging"].(bool); ok {
config.Server.HTTPLogging = v
}
}
// Apply runner settings
if runnerTable, ok := values["runner"].(map[string]any); ok {
if v, ok := runnerTable["pool_size"].(float64); ok {
config.Runner.PoolSize = int(v)
}
}
// Apply dirs settings
if dirsTable, ok := values["dirs"].(map[string]any); ok {
if v, ok := dirsTable["routes"].(string); ok {
config.Dirs.Routes = v
}
if v, ok := dirsTable["static"].(string); ok {
config.Dirs.Static = v
}
if v, ok := dirsTable["fs"].(string); ok {
config.Dirs.FS = v
}
if v, ok := dirsTable["data"].(string); ok {
config.Dirs.Data = v
}
if v, ok := dirsTable["override"].(string); ok {
config.Dirs.Override = v
}
// Handle libs array
if libs, ok := dirsTable["libs"]; ok {
if libsArray := extractStringArray(libs); len(libsArray) > 0 {
config.Dirs.Libs = libsArray
}
}
}
}
// 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
// Key can be a dotted path like "server.port"
func (c *Config) GetCustomValue(key string) any {
parts := strings.Split(key, ".")
if len(parts) == 1 {
return c.values[key]
}
current := c.values
for _, part := range parts[:len(parts)-1] {
next, ok := current[part].(map[string]any)
if !ok {
return nil
}
current = next
}
return current[parts[len(parts)-1]]
}
// GetCustomString returns a custom string configuration value
func (c *Config) GetCustomString(key string, defaultValue string) string {
value := c.GetCustomValue(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.GetCustomValue(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.GetCustomValue(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
}