M cmd/tallyard/main.go => cmd/tallyard/main.go +11 -19
@@ 52,12 52,10 @@ func main() {
panic(err)
}
- if data.Elections == nil {
- data.Elections = election.NewElectionsMap()
+ elections, err := election.GetElections()
+ if err != nil {
+ panic(err)
}
- data.Elections.SetSave(func() {
- data.Save()
- })
syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnEvent(client.Store.(*mautrix.InMemoryStore).UpdateState)
@@ 67,8 65,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnCreateElectionMessage(evt, data.Elections)
- data.Save()
+ election.OnCreateElectionMessage(evt, elections)
})
syncer.OnEventType(election.JoinElectionMessage, func(source mautrix.EventSource, evt *event.Event) {
DebugCB(source, evt)
@@ 76,8 73,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnJoinElectionMessage(client, evt, data.Elections)
- data.Save()
+ election.OnJoinElectionMessage(client, evt, elections)
})
syncer.OnEventType(election.StartElectionMessage, func(source mautrix.EventSource, evt *event.Event) {
DebugCB(source, evt)
@@ 85,8 81,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnStartElectionMessage(client, evt, data.Elections)
- data.Save()
+ election.OnStartElectionMessage(client, evt, elections)
})
syncer.OnEventType(election.EvalMessage, func(source mautrix.EventSource, evt *event.Event) {
DebugCB(source, evt)
@@ 94,8 89,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnEvalMessage(client, evt, data.Elections)
- data.Save()
+ election.OnEvalMessage(client, evt, elections)
})
syncer.OnEventType(election.SumMessage, func(source mautrix.EventSource, evt *event.Event) {
DebugCB(source, evt)
@@ 103,8 97,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnSumMessage(client, evt, data.Elections)
- data.Save()
+ election.OnSumMessage(client, evt, elections)
})
syncer.OnEventType(election.ResultMessage, func(source mautrix.EventSource, evt *event.Event) {
DebugCB(source, evt)
@@ 112,8 105,7 @@ func main() {
log.Debug("redacted")
return
}
- election.OnResultMessage(client, evt, data.Elections)
- data.Save()
+ election.OnResultMessage(client, evt, elections)
})
go func() {
@@ 128,7 120,7 @@ func main() {
}
}()
- el := ui.TUI(client, data.Elections)
+ el := ui.TUI(client, elections)
if el == nil || el.LocalVoter.Ballot == nil {
// user likely hit C-c
return
@@ 136,8 128,8 @@ func main() {
el.Lock()
el.LocalVoter.Poly = math.NewRandomPoly(uint(len(*el.FinalVoters)-1), 1024, *el.LocalVoter.Ballot)
- el.Save()
el.Unlock()
+ el.Save()
// TODO we may not have all voters' info
err = el.SendEvals(client)
M election/map.go => election/map.go +44 -2
@@ 2,9 2,15 @@ package election
import (
"encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
"sort"
"sync"
+ log "github.com/sirupsen/logrus"
+
+ "github.com/kyoh86/xdg"
"maunium.net/go/mautrix/id"
)
@@ 16,12 22,47 @@ type ElectionsMap struct {
Save func()
}
+var electionsFname = xdg.DataHome() + "/tallyard/elections.json"
+
+func GetElections() (elections *ElectionsMap, err error) {
+ elections = NewElectionsMap()
+ if _, err = os.Stat(electionsFname); os.IsNotExist(err) {
+ return elections, 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)
+ if err != nil {
+ return nil, fmt.Errorf("error unmarshalling data file: %s", err)
+ }
+ return
+}
+
func NewElectionsMap() *ElectionsMap {
- return &ElectionsMap{
+ em := &ElectionsMap{
M: make(map[id.EventID]*Election),
L: make([]*Election, 0),
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) {
@@ 38,6 79,7 @@ func (em *ElectionsMap) UnmarshalJSON(b []byte) error {
}
em.Joins = make(map[id.EventID]*Voter)
for _, el := range em.M {
+ el.Save = em.Save
for joinEventId, voter := range el.Joins {
em.Joins[joinEventId] = voter
}
@@ 78,13 120,13 @@ func (em *ElectionsMap) GetI(i int) *Election {
func (em *ElectionsMap) SetIfNotExists(createEventID id.EventID, el *Election) {
em.Lock()
+ defer em.Save()
defer em.Unlock()
_, exists := em.M[createEventID]
if exists {
return
}
em.set(createEventID, el)
- em.Save()
}
func (em *ElectionsMap) set(createEventID id.EventID, el *Election) {
M election/voter.go => election/voter.go +3 -3
@@ 83,6 83,7 @@ func (el *Election) JoinElection(client *mautrix.Client) error {
}
el.Lock()
+ defer el.Save()
defer el.Unlock()
resp, err := client.SendMessageEvent(el.RoomID, JoinElectionMessage, JoinElectionContent{
@@ 109,13 110,13 @@ func (el *Election) JoinElection(client *mautrix.Client) error {
PrivKey: *privKey,
}
el.Joins[resp.EventID] = el.LocalVoter.Voter
- el.Save()
return nil
}
func (el *Election) StartElection(client *mautrix.Client) error {
// TODO err from this function if we didn't create the election
el.Lock()
+ defer el.Save()
defer el.Unlock()
userIdMap := make(map[id.UserID]*Voter)
// one vote per userID
@@ 152,7 153,6 @@ func (el *Election) StartElection(client *mautrix.Client) error {
// OnStartElectionMessage(client, startEvt, elections)
el.StartEvt = startEvt
el.FinalVoters = &voters
- el.Save()
return nil
}
@@ 182,6 182,7 @@ func (el *Election) WaitForVoters(client *mautrix.Client) error {
func (el *Election) SendEvals(client *mautrix.Client) error {
// this assumes we have all needed voter data
el.Lock()
+ defer el.Save()
defer el.Unlock()
content := EvalMessageContent{
JoinEventId: el.LocalVoter.JoinEvt.ID,
@@ 205,7 206,6 @@ func (el *Election) SendEvals(client *mautrix.Client) error {
el.LocalVoter.Eval = nil
return err
}
- el.Save()
return nil
}
M matrix/data.go => matrix/data.go +9 -11
@@ 10,25 10,23 @@ import (
"github.com/kyoh86/xdg"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
- "tallyard.xyz/election"
)
type Data struct {
- AccessToken string `json:"access_token"`
- DeviceID id.DeviceID `json:"device_id"`
- Elections *election.ElectionsMap `json:"elections,omitempty"`
- Homeserver string `json:"homeserver"`
- UserID id.UserID `json:"user_id"`
- Username string `json:"username"`
+ AccessToken string `json:"access_token"`
+ DeviceID id.DeviceID `json:"device_id"`
+ Homeserver string `json:"homeserver"`
+ UserID id.UserID `json:"user_id"`
+ Username string `json:"username"`
}
var dataFname = xdg.DataHome() + "/tallyard/data.json"
func GetData() (data *Data, err error) {
if _, err = os.Stat(dataFname); os.IsNotExist(err) {
- data, err = InquireForData()
+ data, err = inquireForData()
if err == nil {
- data.Save()
+ data.save()
}
return data, err
}
@@ 57,7 55,7 @@ func stripNewline(s string) string {
return s
}
-func InquireForData() (data *Data, err error) {
+func inquireForData() (data *Data, err error) {
var (
password string
reader = bufio.NewReader(os.Stdin)
@@ 113,7 111,7 @@ func InquireForData() (data *Data, err error) {
return data, nil
}
-func (data *Data) Save() error {
+func (data *Data) save() error {
jsonBytes, err := json.Marshal(*data)
if err != nil {
return err