aboutsummaryrefslogtreecommitdiffstats
path: root/operations/label_change.go
blob: 26e7e94c50b8d64a49e586d4b83e4b2f7796b4a2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package operations

import (
	"fmt"
	"io"
	"io/ioutil"
	"sort"

	"github.com/MichaelMure/git-bug/bug"
)

var _ bug.Operation = LabelChangeOperation{}

// LabelChangeOperation define a Bug operation to add or remove labels
type LabelChangeOperation struct {
	bug.OpBase
	Added   []bug.Label `json:"added"`
	Removed []bug.Label `json:"removed"`
}

// Apply apply the operation
func (op LabelChangeOperation) Apply(snapshot bug.Snapshot) bug.Snapshot {
	// Add in the set
AddLoop:
	for _, added := range op.Added {
		for _, label := range snapshot.Labels {
			if label == added {
				// Already exist
				continue AddLoop
			}
		}

		snapshot.Labels = append(snapshot.Labels, added)
	}

	// Remove in the set
	for _, removed := range op.Removed {
		for i, label := range snapshot.Labels {
			if label == removed {
				snapshot.Labels[i] = snapshot.Labels[len(snapshot.Labels)-1]
				snapshot.Labels = snapshot.Labels[:len(snapshot.Labels)-1]
			}
		}
	}

	// Sort
	sort.Slice(snapshot.Labels, func(i, j int) bool {
		return string(snapshot.Labels[i]) < string(snapshot.Labels[j])
	})

	return snapshot
}

func NewLabelChangeOperation(author bug.Person, added, removed []bug.Label) LabelChangeOperation {
	return LabelChangeOperation{
		OpBase:  bug.NewOpBase(bug.LabelChangeOp, author),
		Added:   added,
		Removed: removed,
	}
}

// ChangeLabels is a convenience function to apply the operation
func ChangeLabels(out io.Writer, b bug.Interface, author bug.Person, add, remove []string) error {
	// TODO: return a channel of result (like MergeAll) instead of formatting the result for the upper layers
	var added, removed []bug.Label

	if out == nil {
		out = ioutil.Discard
	}

	snap := b.Compile()

	for _, str := range add {
		label := bug.Label(str)

		// check for duplicate
		if labelExist(added, label) {
			fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
			continue
		}

		// check that the label doesn't already exist
		if labelExist(snap.Labels, label) {
			fmt.Fprintf(out, "label \"%s\" is already set on this bug\n", str)
			continue
		}

		added = append(added, label)
	}

	for _, str := range remove {
		label := bug.Label(str)

		// check for duplicate
		if labelExist(removed, label) {
			fmt.Fprintf(out, "label \"%s\" is a duplicate\n", str)
			continue
		}

		// check that the label actually exist
		if !labelExist(snap.Labels, label) {
			fmt.Fprintf(out, "label \"%s\" doesn't exist on this bug\n", str)
			continue
		}

		removed = append(removed, label)
	}

	if len(added) == 0 && len(removed) == 0 {
		return fmt.Errorf("no label added or removed")
	}

	labelOp := NewLabelChangeOperation(author, added, removed)

	b.Append(labelOp)

	return nil
}

func labelExist(labels []bug.Label, label bug.Label) bool {
	for _, l := range labels {
		if l == label {
			return true
		}
	}

	return false
}