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	snapshot.Timeline = append(snapshot.Timeline, op)
 59}
 60
 61func (op *LabelChangeOperation) Validate() error {
 62	if err := opBaseValidate(op, LabelChangeOp); err != nil {
 63		return err
 64	}
 65
 66	for _, l := range op.Added {
 67		if err := l.Validate(); err != nil {
 68			return errors.Wrap(err, "added label")
 69		}
 70	}
 71
 72	for _, l := range op.Removed {
 73		if err := l.Validate(); err != nil {
 74			return errors.Wrap(err, "removed label")
 75		}
 76	}
 77
 78	if len(op.Added)+len(op.Removed) <= 0 {
 79		return fmt.Errorf("no label change")
 80	}
 81
 82	return nil
 83}
 84
 85func NewLabelChangeOperation(author Person, unixTime int64, added, removed []Label) *LabelChangeOperation {
 86	return &LabelChangeOperation{
 87		OpBase:  newOpBase(LabelChangeOp, author, unixTime),
 88		Added:   added,
 89		Removed: removed,
 90	}
 91}
 92
 93// ChangeLabels is a convenience function to apply the operation
 94func ChangeLabels(b Interface, author Person, unixTime int64, add, remove []string) ([]LabelChangeResult, error) {
 95	var added, removed []Label
 96	var results []LabelChangeResult
 97
 98	snap := b.Compile()
 99
100	for _, str := range add {
101		label := Label(str)
102
103		// check for duplicate
104		if labelExist(added, label) {
105			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
106			continue
107		}
108
109		// check that the label doesn't already exist
110		if labelExist(snap.Labels, label) {
111			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAlreadySet})
112			continue
113		}
114
115		added = append(added, label)
116		results = append(results, LabelChangeResult{Label: label, Status: LabelChangeAdded})
117	}
118
119	for _, str := range remove {
120		label := Label(str)
121
122		// check for duplicate
123		if labelExist(removed, label) {
124			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDuplicateInOp})
125			continue
126		}
127
128		// check that the label actually exist
129		if !labelExist(snap.Labels, label) {
130			results = append(results, LabelChangeResult{Label: label, Status: LabelChangeDoesntExist})
131			continue
132		}
133
134		removed = append(removed, label)
135		results = append(results, LabelChangeResult{Label: label, Status: LabelChangeRemoved})
136	}
137
138	if len(added) == 0 && len(removed) == 0 {
139		return results, fmt.Errorf("no label added or removed")
140	}
141
142	labelOp := NewLabelChangeOperation(author, unixTime, added, removed)
143
144	if err := labelOp.Validate(); err != nil {
145		return nil, err
146	}
147
148	b.Append(labelOp)
149
150	return results, nil
151}
152
153func labelExist(labels []Label, label Label) bool {
154	for _, l := range labels {
155		if l == label {
156			return true
157		}
158	}
159
160	return false
161}
162
163type LabelChangeStatus int
164
165const (
166	_ LabelChangeStatus = iota
167	LabelChangeAdded
168	LabelChangeRemoved
169	LabelChangeDuplicateInOp
170	LabelChangeAlreadySet
171	LabelChangeDoesntExist
172)
173
174type LabelChangeResult struct {
175	Label  Label
176	Status LabelChangeStatus
177}
178
179func (l LabelChangeResult) String() string {
180	switch l.Status {
181	case LabelChangeAdded:
182		return fmt.Sprintf("label %s added", l.Label)
183	case LabelChangeRemoved:
184		return fmt.Sprintf("label %s removed", l.Label)
185	case LabelChangeDuplicateInOp:
186		return fmt.Sprintf("label %s is a duplicate", l.Label)
187	case LabelChangeAlreadySet:
188		return fmt.Sprintf("label %s was already set", l.Label)
189	case LabelChangeDoesntExist:
190		return fmt.Sprintf("label %s doesn't exist on this bug", l.Label)
191	default:
192		panic(fmt.Sprintf("unknown label change status %v", l.Status))
193	}
194}