X-Git-Url: https://ruderich.org/simon/gitweb/?p=nsscash%2Fnsscash.git;a=blobdiff_plain;f=state.go;fp=state.go;h=b0dec1c2d47673fffa5eb81ee8e3d3113fce689f;hp=0000000000000000000000000000000000000000;hb=92afde4e875a96e1ab865e29b9f0d11b08d7db1c;hpb=748846b9a65726fd19d43b45a4db68ef4c2e77f4 diff --git a/state.go b/state.go new file mode 100644 index 0000000..b0dec1c --- /dev/null +++ b/state.go @@ -0,0 +1,98 @@ +// Read and write the state file used to keep data over multiple runs + +// Copyright (C) 2019 Simon Ruderich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package main + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "time" +) + +type State struct { + LastModified map[string]time.Time + + // Copy of LastModified to write the state file only on changes + origLastModified map[string]time.Time +} + +func LoadState(path string) (*State, error) { + var state State + + x, err := ioutil.ReadFile(path) + if err != nil { + // It's fine if the state file does not exist yet, we'll + // create it later when writing the state + if !os.IsNotExist(err) { + return nil, err + } + } else { + err := json.Unmarshal(x, &state) + if err != nil { + return nil, err + } + } + + if state.LastModified == nil { + state.LastModified = make(map[string]time.Time) + } + + state.origLastModified = make(map[string]time.Time) + for k, v := range state.LastModified { + state.origLastModified[k] = v + } + + return &state, nil +} + +func WriteStateIfChanged(path string, state *State) error { + // State hasn't changed, nothing to do + if reflect.DeepEqual(state.LastModified, state.origLastModified) { + return nil + } + + x, err := json.Marshal(state) + if err != nil { + return err + } + + // Write the file in an atomic fashion by creating a temporary file + // and renaming it over the target file + + dir := filepath.Dir(path) + name := filepath.Base(path) + + f, err := ioutil.TempFile(dir, "tmp-"+name+"-") + if err != nil { + return err + } + defer os.Remove(f.Name()) + defer f.Close() + + _, err = f.Write(x) + if err != nil { + return err + } + err = f.Sync() + if err != nil { + return err + } + return os.Rename(f.Name(), path) +}