label_change.go

  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, unixTime int64, added, removed []bug.Label) LabelChangeOperation {
 78	return LabelChangeOperation{
 79		OpBase:  bug.NewOpBase(bug.LabelChangeOp, author, unixTime),
 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, unixTime int64, 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, unixTime, 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}
170
171func (l LabelChangeResult) String() string {
172	switch l.Status {
173	case LabelChangeAdded:
174		return fmt.Sprintf("label %s added", l.Label)
175	case LabelChangeRemoved:
176		return fmt.Sprintf("label %s removed", l.Label)
177	case LabelChangeDuplicateInOp:
178		return fmt.Sprintf("label %s is a duplicate", l.Label)
179	case LabelChangeAlreadySet:
180		return fmt.Sprintf("label %s was already set", l.Label)
181	case LabelChangeDoesntExist:
182		return fmt.Sprintf("label %s doesn't exist on this bug", l.Label)
183	default:
184		panic(fmt.Sprintf("unknown label change status %v", l.Status))
185	}
186}