0.0.0.0 Initial commit, basic core functionality
This commit is contained in:
149
config.go
Normal file
149
config.go
Normal file
@ -0,0 +1,149 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/LixenWraith/tinytoml"
|
||||
)
|
||||
|
||||
type cliArg struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
func Load(path string, config interface{}, args []string) (bool, error) {
|
||||
if config == nil {
|
||||
return false, fmt.Errorf("config cannot be nil")
|
||||
}
|
||||
|
||||
configExists := false
|
||||
if stat, err := os.Stat(path); err == nil && !stat.IsDir() {
|
||||
configExists = true
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
if err := tinytoml.Unmarshal(data, config); err != nil {
|
||||
return false, fmt.Errorf("failed to parse config file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
overrides, err := parseArgs(args)
|
||||
if err != nil {
|
||||
return configExists, fmt.Errorf("failed to parse CLI args: %w", err)
|
||||
}
|
||||
|
||||
if err := mergeConfig(config, overrides); err != nil {
|
||||
return configExists, fmt.Errorf("failed to merge CLI args: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return configExists, nil
|
||||
}
|
||||
|
||||
func Save(path string, config interface{}) error {
|
||||
v := reflect.ValueOf(config)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return fmt.Errorf("config must be a struct or pointer to struct")
|
||||
}
|
||||
|
||||
data, err := tinytoml.Marshal(v.Interface())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
tempFile := path + ".tmp"
|
||||
if err := os.WriteFile(tempFile, data, 0644); err != nil {
|
||||
return fmt.Errorf("failed to write temp config file: %w", err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tempFile, path); err != nil {
|
||||
os.Remove(tempFile)
|
||||
return fmt.Errorf("failed to save config file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseArgs(args []string) (map[string]interface{}, error) {
|
||||
parsed := make([]cliArg, 0, len(args))
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
if !strings.HasPrefix(arg, "--") {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimPrefix(arg, "--")
|
||||
if i+1 >= len(args) || strings.HasPrefix(args[i+1], "--") {
|
||||
parsed = append(parsed, cliArg{key: key, value: "true"})
|
||||
continue
|
||||
}
|
||||
|
||||
parsed = append(parsed, cliArg{key: key, value: args[i+1]})
|
||||
i++
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
for _, arg := range parsed {
|
||||
keys := strings.Split(arg.key, ".")
|
||||
current := result
|
||||
for i, k := range keys[:len(keys)-1] {
|
||||
if _, exists := current[k]; !exists {
|
||||
current[k] = make(map[string]interface{})
|
||||
}
|
||||
if nested, ok := current[k].(map[string]interface{}); ok {
|
||||
current = nested
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid nested key at %s", strings.Join(keys[:i+1], "."))
|
||||
}
|
||||
}
|
||||
|
||||
lastKey := keys[len(keys)-1]
|
||||
if val, err := strconv.ParseBool(arg.value); err == nil {
|
||||
current[lastKey] = val
|
||||
} else if val, err := strconv.ParseInt(arg.value, 10, 64); err == nil {
|
||||
current[lastKey] = val
|
||||
} else if val, err := strconv.ParseFloat(arg.value, 64); err == nil {
|
||||
current[lastKey] = val
|
||||
} else {
|
||||
current[lastKey] = arg.value
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func mergeConfig(base interface{}, override map[string]interface{}) error {
|
||||
baseValue := reflect.ValueOf(base)
|
||||
if baseValue.Kind() != reflect.Ptr || baseValue.IsNil() {
|
||||
return fmt.Errorf("base config must be a non-nil pointer")
|
||||
}
|
||||
|
||||
data, err := json.Marshal(override)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal override values: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, base); err != nil {
|
||||
return fmt.Errorf("failed to merge override values: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user