@@ 72,7 72,7 @@ func main() {
}()
client.Store = store
- elections, err := election.GetElections()
+ elections, err := election.GetElectionsMap()
if err != nil {
panic(err)
}
@@ 17,69 17,51 @@ import (
type ElectionsMap struct {
sync.RWMutex
- M map[id.EventID]*Election
- // by room, sorted list of elections by CreationTimestamp (newest to oldest)
- L map[id.RoomID][]*Election
- Ltime time.Time // last time list was update
-
- Joins map[id.EventID]*Voter
- Save func()
+ // The version of the elections map. If the version in the file doesn't
+ // match the current version then we can give a clear error message
+ // telling the user to update their file to the current version. The
+ // version in the file should only be different if there has been a
+ // breaking change to the elections map format.
+ Version int `json:"version"`
+
+ // Maps election create event IDs to the corresponding election.
+ M map[id.EventID]*Election `json:"elections"`
+
+ // Maps room to a list of the room's elections, reverse sorted by
+ // CreationTimestamp (i.e. newest to oldest). Here for convenience.
+ L map[id.RoomID][]*Election `json:"-"`
+ // The latest time L was updated
+ Ltime time.Time `json:"-"`
+
+ // maps join event ID to voter. This is a convenience in scenarios when
+ // we don't know the exact election
+ Joins map[id.EventID]*Voter `json:"-"`
}
+const electionsMapVersion = 1
+
var electionsFname = xdg.DataHome() + "/tallyard/elections.json"
-func GetElections() (elections *ElectionsMap, err error) {
- elections = NewElectionsMap()
+func GetElectionsMap() (em *ElectionsMap, err error) {
+ em = &ElectionsMap{}
if _, err = os.Stat(electionsFname); os.IsNotExist(err) {
- return elections, nil
+ return newElectionsMap(), nil
}
jsonBytes, err := ioutil.ReadFile(electionsFname)
if err != nil {
return nil, fmt.Errorf("error reading data file %s: %s", electionsFname, err)
}
- err = json.Unmarshal(jsonBytes, elections)
+ err = json.Unmarshal(jsonBytes, em)
if err != nil {
return nil, fmt.Errorf("error unmarshalling data file: %s", err)
}
- return
-}
-
-func NewElectionsMap() *ElectionsMap {
- em := &ElectionsMap{
- M: make(map[id.EventID]*Election),
- L: make(map[id.RoomID][]*Election, 0),
- Ltime: time.Now(),
- Joins: make(map[id.EventID]*Voter),
- }
- em.Save = func() {
- em.RLock()
- defer em.RUnlock()
- for _, el := range em.M {
- el.RLock()
- defer el.RUnlock()
- }
- jsonBytes, err := json.Marshal(em)
- if err != nil {
- log.Errorf("couldn't marshal elections: %s", err)
- }
- err = ioutil.WriteFile(electionsFname, jsonBytes, 0600)
- if err != nil {
- log.Errorf("couldn't save elections: %s", err)
- }
- }
- return em
-}
-
-func (em *ElectionsMap) MarshalJSON() ([]byte, error) {
- return json.Marshal(em.M)
-}
-
-func (em *ElectionsMap) UnmarshalJSON(b []byte) error {
- if err := json.Unmarshal(b, &em.M); err != nil {
- return err
+ if em.Version != electionsMapVersion {
+ return nil, fmt.Errorf("Your stored elections schema version (%d) is incompitable with the latest schema version (%d).",
+ em.Version, electionsMapVersion)
}
em.L = make(map[id.RoomID][]*Election, 0)
+ em.Ltime = time.Now()
for createEventId, el := range em.M {
em.insort(createEventId, el)
}
@@ 90,7 72,36 @@ func (em *ElectionsMap) UnmarshalJSON(b []byte) error {
em.Joins[joinEventId] = voter
}
}
- return nil
+ return
+}
+
+func newElectionsMap() *ElectionsMap {
+ return &ElectionsMap{
+ Version: electionsMapVersion,
+ M: make(map[id.EventID]*Election),
+ L: make(map[id.RoomID][]*Election),
+ Ltime: time.Now(),
+ Joins: make(map[id.EventID]*Voter),
+ }
+}
+
+func (em *ElectionsMap) Save() {
+ em.RLock()
+ defer em.RUnlock()
+ for _, el := range em.M {
+ el.RLock()
+ defer el.RUnlock()
+ }
+ jsonBytes, err := json.Marshal(em)
+ if err != nil {
+ log.Errorf("couldn't marshal elections: %s", err)
+ return
+ }
+ err = ioutil.WriteFile(electionsFname, jsonBytes, 0600)
+ if err != nil {
+ log.Errorf("couldn't save elections: %s", err)
+ return
+ }
}
func (em *ElectionsMap) Get(createEventID id.EventID) *Election {