package main import ( "encoding/binary" "encoding/json" "fmt" "go.etcd.io/bbolt" ) type taskType struct { What string `json:"what"` Done bool `json:"done"` Index uint64 `json:"index"` } // itob returns an 8-byte big endian representation of v. func itob(v uint64) []byte { b := make([]byte, 8) binary.BigEndian.PutUint64(b, v) return b } func (ctrl controller) cmdAdd(task string) error { return ctrl.db.Update(func(tx *bbolt.Tx) error { taskBucket := tx.Bucket(ctrl.tasksBucketName) // Update call, so ignore the error check. See // pkg.go.dev/go.etcd.io/bbolt#section-readme index, _ := taskBucket.NextSequence() t := taskType{ What: task, Done: false, Index: index, } buf, err := json.Marshal(t) if err != nil { return fmt.Errorf("can't marshal: %w", err) } return taskBucket.Put(itob(index), buf) }) } func (ctrl controller) toggleDo(taskIndex int, status bool) error { return ctrl.db.Update(func(tx *bbolt.Tx) error { taskBucket := tx.Bucket(ctrl.tasksBucketName) ttBytes := taskBucket.Get(itob(uint64(taskIndex))) var tt taskType if err := json.Unmarshal(ttBytes, &tt); err != nil { return fmt.Errorf("can't unmarshal: %w", err) } tt.Done = status buf, err := json.Marshal(tt) if err != nil { return fmt.Errorf("can't marshal: %w", err) } return taskBucket.Put(itob(uint64(taskIndex)), buf) }) } func (ctrl controller) cmdDo(taskIndex int) error { return ctrl.toggleDo(taskIndex, true) } func (ctrl controller) cmdUndo(taskIndex int) error { return ctrl.toggleDo(taskIndex, false) } func (ctrl controller) cmdList() error { return ctrl.db.View(func(tx *bbolt.Tx) error { // Assume bucket exists and has keys b := tx.Bucket([]byte(ctrl.tasksBucketName)) b.ForEach(func(k, v []byte) error { index := binary.BigEndian.Uint64(k) var tt taskType if err := json.Unmarshal(v, &tt); err != nil { return fmt.Errorf("can't unmarshal: %w", err) } status := "TODO" if tt.Done { status = "DONE" } fmt.Printf("%d. (%s) %s\n", index, status, tt.What) return nil }) return nil }) }