291 lines
5.9 KiB
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
|
|
}
|
|
|
|
// NewConfig 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
|
|
}
|