1package bug
2
3import (
4 "fmt"
5 "sort"
6
7 "github.com/MichaelMure/git-bug/util/git"
8 "github.com/pkg/errors"
9)
10
11var _ Operation = LabelChangeOperation{}
12
13// LabelChangeOperation define a Bug operation to add or remove labels
14type LabelChangeOperation struct {
15 *OpBase
16 Added []Label `json:"added"`
17 Removed []Label `json:"removed"`
18}
19
20func (op LabelChangeOperation) base() *OpBase {
21 return op.OpBase
22}
23
24func (op LabelChangeOperation) Hash() (git.Hash, error) {
25 return hashOperation(op)
26}
27
28// Apply apply the operation
29func (op LabelChangeOperation) Apply(snapshot *Snapshot) {
30 // Add in the set
31AddLoop:
32 for _, added := range op.Added {
33 for _, label := range snapshot.Labels {
34 if label == added {
35 // Already exist
36 continue AddLoop
37 }
38 }
39
40 snapshot.Labels = append(snapshot.Labels, added)
41 }
42
43 // Remove in the set
44 for _, removed := range op.Removed {
45 for i, label := range snapshot.Labels {
46 if label == removed {
47 snapshot.Labels[i] = snapshot.Labels[len(snapshot.Labels)-1]
48 snapshot.Labels = snapshot.Labels[:len(snapshot.Labels)-1]
49 }
50 }
51 }
52
53 // Sort
54 sort.Slice(snapshot.Labels, func(i, j int) bool {
55 return string(snapshot.Labels[i]) < string(snapshot.Labels[j])
56 })
57}
58
59func (op LabelChangeOperation) Validate() error {
60 if err := opBaseValidate(op, LabelChangeOp); err != nil {
61 return err
62 }
63
64 for _, l := range op.Added {
65 if err := l.Validate(); err != nil {
66 return errors.Wrap(err, "added label")
67 }
68 }
69
70 for _, l := range op.Removed {
71 if err := l.Validate(); err != nil {
72 return errors.Wrap(err, "removed label")
73 }
74 }
75
76 if len(op.Added)+len(op.Removed) <= 0 {
77 return fmt.Errorf("no label change")
78 }
79
80 return nil
81}
82
83func NewLabelChangeOperation(author Person, unixTime int64, added, removed []Label) LabelChangeOperation {
84 return LabelChangeOperation{
85 OpBase: newOpBase(LabelChangeOp, author, unixTime),
86 Added: added,
87 Removed: removed,
88 }
89}
90
91// ChangeLabels is a convenience function to apply the operation
92func ChangeLabels(b Interface, author Person, unixTime int64, add, remove []string) ([]LabelChangeResult, error) {
93 var added, removed []Label
94 var results []LabelChangeResult
95
96 snap := b.Compile()
97
98 for _, str := range add {
99 label := Label(str)
100
101 // check for duplicate
102 if labelExist(added, label) {
103 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
104 continue
105 }
106
107 // check that the label doesn't already exist
108 if labelExist(snap.Labels, label) {
109 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAlreadySet})
110 continue
111 }
112
113 added = append(added, label)
114 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAdded})
115 }
116
117 for _, str := range remove {
118 label := Label(str)
119
120 // check for duplicate
121 if labelExist(removed, label) {
122 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
123 continue
124 }
125
126 // check that the label actually exist
127 if !labelExist(snap.Labels, label) {
128 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDoesntExist})
129 continue
130 }
131
132 removed = append(removed, label)
133 results = append(results, LabelChangeResult{Label: label, Status: LabelChangeRemoved})
134 }
135
136 if len(added) == 0 && len(removed) == 0 {
137 return results, fmt.Errorf("no label added or removed")
138 }
139
140 labelOp := NewLabelChangeOperation(author, unixTime, added, removed)
141
142 if err := labelOp.Validate(); err != nil {
143 return nil, err
144 }
145
146 b.Append(labelOp)
147
148 return results, nil
149}
150
151func labelExist(labels []Label, label Label) bool {
152 for _, l := range labels {
153 if l == label {
154 return true
155 }
156 }
157
158 return false
159}
160
161type LabelChangeStatus int
162
163const (
164 _ LabelChangeStatus = iota
165 LabelChangeAdded
166 LabelChangeRemoved
167 LabelChangeDuplicateInOp
168 LabelChangeAlreadySet
169 LabelChangeDoesntExist
170)
171
172type LabelChangeResult struct {
173 Label Label
174 Status LabelChangeStatus
175}
176
177func (l LabelChangeResult) String() string {
178 switch l.Status {
179 case LabelChangeAdded:
180 return fmt.Sprintf("label %s added", l.Label)
181 case LabelChangeRemoved:
182 return fmt.Sprintf("label %s removed", l.Label)
183 case LabelChangeDuplicateInOp:
184 return fmt.Sprintf("label %s is a duplicate", l.Label)
185 case LabelChangeAlreadySet:
186 return fmt.Sprintf("label %s was already set", l.Label)
187 case LabelChangeDoesntExist:
188 return fmt.Sprintf("label %s doesn't exist on this bug", l.Label)
189 default:
190 panic(fmt.Sprintf("unknown label change status %v", l.Status))
191 }
192}