]> ruderich.org/simon Gitweb - nsscash/nsscash.git/blob - state.go
nsscash: main_test: add special tests
[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         // Key is File.Url
30         LastModified map[string]time.Time
31         Checksum     map[string]string // SHA512 in hex
32 }
33
34 func LoadState(path string) (*State, error) {
35         var state State
36
37         x, err := ioutil.ReadFile(path)
38         if err != nil {
39                 // It's fine if the state file does not exist yet, we'll
40                 // create it later when writing the state
41                 if !os.IsNotExist(err) {
42                         return nil, err
43                 }
44         } else {
45                 err := json.Unmarshal(x, &state)
46                 if err != nil {
47                         return nil, err
48                 }
49         }
50
51         if state.LastModified == nil {
52                 state.LastModified = make(map[string]time.Time)
53         }
54         if state.Checksum == nil {
55                 state.Checksum = make(map[string]string)
56         }
57
58         return &state, nil
59 }
60
61 func WriteState(path string, state *State) error {
62         // Update the state file even if nothing has changed to provide a
63         // simple way to check if nsscash ran successfully (the state is only
64         // updated if there were no errors)
65
66         x, err := json.Marshal(state)
67         if err != nil {
68                 return err
69         }
70
71         // Write the file in an atomic fashion by creating a temporary file
72         // and renaming it over the target file
73
74         dir := filepath.Dir(path)
75         name := filepath.Base(path)
76
77         f, err := ioutil.TempFile(dir, "tmp-"+name+"-")
78         if err != nil {
79                 return err
80         }
81         defer os.Remove(f.Name())
82         defer f.Close()
83
84         _, err = f.Write(x)
85         if err != nil {
86                 return err
87         }
88         err = f.Sync()
89         if err != nil {
90                 return err
91         }
92         return os.Rename(f.Name(), path)
93 }