op_label_change.go

  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}