op_label_change.go

  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}