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(¤t).Elem().FieldByName(field.Name).Type() { nv := reflect.New(reflect.ValueOf(¤t).Elem().FieldByName(field.Name).Type()) mv, _ := json.Marshal(v) json.Unmarshal(mv, nv.Interface()) v = nv.Elem().Interface() } reflect.ValueOf(¤t).Elem().FieldByName(field.Name).Set(reflect.ValueOf(v)) } } return ¤t }