safing-portbase/config/persistence.go
2019-06-27 13:29:56 +02:00

125 lines
2.4 KiB
Go

package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/safing/portbase/log"
)
var (
configFilePath string
)
func loadConfig() error {
data, err := ioutil.ReadFile(configFilePath)
if err != nil {
return err
}
m, err := JSONToMap(data)
if err != nil {
return err
}
return setConfig(m)
}
func saveConfig() (err error) {
data, err := MapToJSON(userConfig)
if err == nil {
err = ioutil.WriteFile(configFilePath, data, 0600)
}
if err != nil {
log.Errorf("config: failed to save config: %s", err)
}
return err
}
// JSONToMap parses and flattens a hierarchical json object.
func JSONToMap(jsonData []byte) (map[string]interface{}, error) {
loaded := make(map[string]interface{})
err := json.Unmarshal(jsonData, &loaded)
if err != nil {
return nil, err
}
flatten(loaded, loaded, "")
return loaded, nil
}
func flatten(rootMap, subMap map[string]interface{}, subKey string) {
for key, entry := range subMap {
// get next level key
subbedKey := key
if subKey != "" {
subbedKey = fmt.Sprintf("%s/%s", subKey, key)
}
// check for next subMap
nextSub, ok := entry.(map[string]interface{})
if ok {
flatten(rootMap, nextSub, subbedKey)
delete(rootMap, key)
} else if subKey != "" {
// only set if not on root level
rootMap[subbedKey] = entry
}
}
}
// MapToJSON expands a flattened map and returns it as json.
func MapToJSON(mapData map[string]interface{}) ([]byte, error) {
configLock.RLock()
defer configLock.RUnlock()
new := make(map[string]interface{})
for key, value := range mapData {
new[key] = value
}
expand(new)
return json.MarshalIndent(new, "", " ")
}
// expand expands a flattened map.
func expand(mapData map[string]interface{}) {
var newMaps []map[string]interface{}
for key, entry := range mapData {
if strings.Contains(key, "/") {
parts := strings.SplitN(key, "/", 2)
if len(parts) == 2 {
// get subMap
var subMap map[string]interface{}
v, ok := mapData[parts[0]]
if ok {
subMap, ok = v.(map[string]interface{})
if !ok {
subMap = make(map[string]interface{})
newMaps = append(newMaps, subMap)
mapData[parts[0]] = subMap
}
} else {
subMap = make(map[string]interface{})
newMaps = append(newMaps, subMap)
mapData[parts[0]] = subMap
}
// set entry
subMap[parts[1]] = entry
// delete entry from
delete(mapData, key)
}
}
}
for _, entry := range newMaps {
expand(entry)
}
}