forked from MichaelMure/git-bug
-
Notifications
You must be signed in to change notification settings - Fork 3
/
checklists.go
226 lines (195 loc) · 4.85 KB
/
checklists.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package bug
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/daedaleanai/git-ticket/config"
"github.com/daedaleanai/git-ticket/repository"
"github.com/daedaleanai/git-ticket/util/colors"
)
type ChecklistState int
const (
TBD ChecklistState = iota
Passed
Failed
NotApplicable
)
type ChecklistQuestion struct {
Question string
Comment string
State ChecklistState
}
type ChecklistSection struct {
Title string
Questions []ChecklistQuestion
}
type Checklist struct {
Label Label
Title string
Sections []ChecklistSection
}
type ChecklistSnapshot struct {
Checklist
LastEdit time.Time
}
var checklistStore map[Label]Checklist
// initChecklistStore attempts to read the checklists configuration out of the
// current repository and use it to initialise the checklistStore
func initChecklistStore() error {
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("unable to get the current working directory: %q", err)
}
repo, err := repository.NewGitRepo(cwd, []repository.ClockLoader{ClockLoader})
if err == repository.ErrNotARepo {
return fmt.Errorf("must be run from within a git repo")
}
checklistData, err := config.GetConfig(repo, "checklists")
if err != nil {
return fmt.Errorf("unable to read checklists config: %q", err)
}
checklistStoreTemp := make(map[Label]Checklist)
err = json.Unmarshal(checklistData, &checklistStoreTemp)
if err != nil {
return fmt.Errorf("unable to load checklists: %q", err)
}
checklistStore = checklistStoreTemp
return nil
}
// ListChecklists returns the list of available checklists
func ListChecklists() (map[Label]Checklist, error) {
if checklistStore == nil {
if err := initChecklistStore(); err != nil {
return map[Label]Checklist{}, err
}
}
return checklistStore, nil
}
// GetChecklist returns a Checklist template out of the store
func GetChecklist(label Label) (Checklist, error) {
if checklistStore == nil {
if err := initChecklistStore(); err != nil {
return Checklist{}, err
}
}
cl, present := checklistStore[label]
if !present {
return cl, fmt.Errorf("invalid checklist %s", label)
}
return cl, nil
}
// GetChecklistLabels returns a slice of all the available checklist labels
func GetChecklistLabels() []Label {
if checklistStore == nil {
if err := initChecklistStore(); err != nil {
return nil
}
}
var labels []Label
for _, cl := range checklistStore {
labels = append(labels, cl.Label)
}
return labels
}
func (s ChecklistState) String() string {
switch s {
case TBD:
return "TBD"
case Passed:
return "PASSED"
case Failed:
return "FAILED"
case NotApplicable:
return "NA"
default:
return "UNKNOWN"
}
}
func (s ChecklistState) ShortString() string {
switch s {
case TBD:
return "TBD"
case Passed:
return "P"
case Failed:
return "F"
case NotApplicable:
return "NA"
default:
return "UNKNOWN"
}
}
func (s ChecklistState) ColorString() string {
switch s {
case TBD:
return colors.Blue("TBD")
case Passed:
return colors.Green("PASSED")
case Failed:
return colors.Red("FAILED")
case NotApplicable:
return "NA"
default:
return "UNKNOWN"
}
}
func StateFromString(str string) (ChecklistState, error) {
cleaned := strings.ToLower(strings.TrimSpace(str))
if strings.HasPrefix("tbd", cleaned) {
return TBD, nil
} else if strings.HasPrefix("passed", cleaned) {
return Passed, nil
} else if strings.HasPrefix("failed", cleaned) {
return Failed, nil
} else if strings.HasPrefix("na", cleaned) {
return NotApplicable, nil
}
return 0, fmt.Errorf("unknown state")
}
func (s ChecklistState) Validate() error {
if s < TBD || s > NotApplicable {
return fmt.Errorf("invalid")
}
return nil
}
// CompoundState returns an overall state for the checklist given the state of
// each of the questions. If any of the questions are Failed then the checklist
// Failed, else if any are TBD it's TBD, else it's Passed
func (c Checklist) CompoundState() ChecklistState {
var tbdCount, failedCount int
for _, s := range c.Sections {
for _, q := range s.Questions {
switch q.State {
case TBD:
tbdCount++
case Failed:
failedCount++
}
}
}
// If at least one question has Failed then return that state
if failedCount > 0 {
return Failed
}
// None have Failed, but if any are still TBD return that
if tbdCount > 0 {
return TBD
}
// None Failed or TBD, all questions are NotApplicable or Passed, return Passed
return Passed
}
func (c Checklist) String() string {
result := fmt.Sprintf("%s [%s]\n", c.Title, c.CompoundState().ColorString())
for sn, s := range c.Sections {
result = result + fmt.Sprintf("#### %s ####\n", s.Title)
for qn, q := range s.Questions {
result = result + fmt.Sprintf("(%d.%d) %s [%s]\n", sn+1, qn+1, q.Question, q.State.ColorString())
if q.Comment != "" {
result = result + fmt.Sprintf("# %s\n", strings.Replace(q.Comment, "\n", "\n# ", -1))
}
}
}
return result
}