]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - state.go
nsscash: write state on each successful run
[nsscash/nsscash.git] / state.go
1 // Read and write the state file used to keep data over multiple runs
2
3 // Copyright (C) 2019  Simon Ruderich
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU Affero General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU Affero General Public License for more details.
14 //
15 // You should have received a copy of the GNU Affero General Public License
16 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18 package main
19
20 import (
21         "encoding/json"
22         "io/ioutil"
23         "os"
24         "path/filepath"
25         "time"
26 )
27
28 type State struct {
29         LastModified map[string]time.Time
30 }
31
32 func LoadState(path string) (*State, error) {
33         var state State
34
35         x, err := ioutil.ReadFile(path)
36         if err != nil {
37                 // It's fine if the state file does not exist yet, we'll
38                 // create it later when writing the state
39                 if !os.IsNotExist(err) {
40                         return nil, err
41                 }
42         } else {
43                 err := json.Unmarshal(x, &state)
44                 if err != nil {
45                         return nil, err
46                 }
47         }
48
49         if state.LastModified == nil {
50                 state.LastModified = make(map[string]time.Time)
51         }
52
53         return &state, nil
54 }
55
56 func WriteState(path string, state *State) error {
57         // Update the state file even if nothing has changed to provide a
58         // simple way to check if nsscash ran successfully (the state is only
59         // updated if there were no errors)
60
61         x, err := json.Marshal(state)
62         if err != nil {
63                 return err
64         }
65
66         // Write the file in an atomic fashion by creating a temporary file
67         // and renaming it over the target file
68
69         dir := filepath.Dir(path)
70         name := filepath.Base(path)
71
72         f, err := ioutil.TempFile(dir, "tmp-"+name+"-")
73         if err != nil {
74                 return err
75         }
76         defer os.Remove(f.Name())
77         defer f.Close()
78
79         _, err = f.Write(x)
80         if err != nil {
81                 return err
82         }
83         err = f.Sync()
84         if err != nil {
85                 return err
86         }
87         return os.Rename(f.Name(), path)
88 }