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