server/settings/diff.go

126 lines
2.9 KiB
Go

package settings
import (
"encoding/json"
"fmt"
"os"
"path"
"reflect"
"strconv"
"strings"
"time"
)
// DiffSettings returns only the fields that differs between the two objects.
func DiffSettings(old, new *Settings) (ret map[string]interface{}) {
ret = map[string]interface{}{}
for _, field := range reflect.VisibleFields(reflect.TypeOf(*old)) {
if !reflect.DeepEqual(reflect.ValueOf(*old).FieldByName(field.Name).Interface(), reflect.ValueOf(*new).FieldByName(field.Name).Interface()) {
name := field.Name
if tag, ok := field.Tag.Lookup("json"); ok {
name = strings.Split(tag, ",")[0]
}
ret[name] = reflect.ValueOf(*new).FieldByName(field.Name).Interface()
}
}
return
}
type NextSettingsFile struct {
Id int64 `json:"id"`
Date time.Time `json:"date"`
Values map[string]interface{} `json:"values"`
}
func ReadNextSettingsFile(filename string, ts int64) (*NextSettingsFile, error) {
fd, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("unable to open(%s): %w", filename, err)
} else {
defer fd.Close()
var s map[string]interface{}
jdec := json.NewDecoder(fd)
if err := jdec.Decode(&s); err != nil {
return nil, fmt.Errorf("an error occurs during JSON decoding of %s: %w", filename, err)
}
return &NextSettingsFile{
Id: ts,
Date: time.Unix(ts, 0),
Values: s,
}, nil
}
}
func ListNextSettingsFiles() ([]*NextSettingsFile, error) {
files, err := os.ReadDir(SettingsDir)
if err != nil {
return nil, err
}
var ret []*NextSettingsFile
for _, file := range files {
if len(file.Name()) < 10 {
continue
}
ts, err := strconv.ParseInt(file.Name()[:10], 10, 64)
if err == nil {
nsf, err := ReadNextSettingsFile(path.Join(SettingsDir, file.Name()), ts)
if err != nil {
return nil, err
}
ret = append(ret, nsf)
}
}
return ret, nil
}
func MergeNextSettingsUntil(until *time.Time) (map[string]interface{}, error) {
nsfs, err := ListNextSettingsFiles()
if err != nil {
return nil, err
}
ret := map[string]interface{}{}
for _, nsf := range nsfs {
if until == nil || time.Unix(nsf.Id, 0).Before(*until) {
for k, v := range nsf.Values {
ret[k] = v
}
}
}
return ret, nil
}
func MergeSettings(current Settings, new map[string]interface{}) *Settings {
for _, field := range reflect.VisibleFields(reflect.TypeOf(current)) {
name := field.Name
if tag, ok := field.Tag.Lookup("json"); ok {
name = strings.Split(tag, ",")[0]
}
if v, ok := new[name]; ok {
if reflect.TypeOf(v) != reflect.ValueOf(&current).Elem().FieldByName(field.Name).Type() {
nv := reflect.New(reflect.ValueOf(&current).Elem().FieldByName(field.Name).Type())
mv, _ := json.Marshal(v)
json.Unmarshal(mv, nv.Interface())
v = nv.Elem().Interface()
}
reflect.ValueOf(&current).Elem().FieldByName(field.Name).Set(reflect.ValueOf(v))
}
}
return &current
}