Files

111 lines
2.5 KiB
Go

package kernel
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/cedar2025/xboard-node/internal/nlog"
"gopkg.in/yaml.v3"
)
// LoadCustomConfig reads a custom config file (JSON or YAML) and returns it
// as a generic map. Returns nil if path is empty or file does not exist.
func LoadCustomConfig(path string) (map[string]any, error) {
if path == "" {
return nil, nil
}
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
nlog.Core().Warn("custom config file not found, skipping", "path", path)
return nil, nil
}
return nil, fmt.Errorf("read custom config %s: %w", path, err)
}
if len(data) == 0 {
return nil, nil
}
result := make(map[string]any)
trimmed := strings.TrimSpace(string(data))
if strings.HasPrefix(trimmed, "{") {
if err := json.Unmarshal(data, &result); err != nil {
return nil, fmt.Errorf("parse custom config as JSON: %w", err)
}
} else {
if err := yaml.Unmarshal(data, &result); err != nil {
return nil, fmt.Errorf("parse custom config as YAML: %w", err)
}
}
nlog.Core().Info("loaded custom config", "path", path, "keys", mapKeys(result))
return result, nil
}
// MergeAppendList appends items from src list into dst, returning the merged list.
// Each item is converted to map[string]any for JSON-marshal compatibility.
func MergeAppendList(dst []map[string]any, srcRaw any) []map[string]any {
srcList, ok := toSliceOfMaps(srcRaw)
if !ok {
return dst
}
return append(dst, srcList...)
}
// MergePrependList prepends items from src list before dst items.
func MergePrependList(dst []map[string]any, srcRaw any) []map[string]any {
srcList, ok := toSliceOfMaps(srcRaw)
if !ok {
return dst
}
return append(srcList, dst...)
}
func toSliceOfMaps(v any) ([]map[string]any, bool) {
if v == nil {
return nil, false
}
switch items := v.(type) {
case []any:
result := make([]map[string]any, 0, len(items))
for _, item := range items {
if m, ok := toMap(item); ok {
result = append(result, m)
}
}
return result, len(result) > 0
case []map[string]any:
return items, len(items) > 0
default:
return nil, false
}
}
func toMap(v any) (map[string]any, bool) {
switch m := v.(type) {
case map[string]any:
return m, true
case map[any]any:
result := make(map[string]any, len(m))
for k, val := range m {
result[fmt.Sprint(k)] = val
}
return result, true
default:
return nil, false
}
}
func mapKeys(m map[string]any) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}