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