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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
package cache
import (
"encoding/gob"
"fmt"
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/entity"
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/lamport"
)
// Package initialisation used to register the type for (de)serialization
func init() {
gob.Register(BugExcerpt{})
}
// BugExcerpt hold a subset of the bug values to be able to sort and filter bugs
// efficiently without having to read and compile each raw bugs.
type BugExcerpt struct {
Id entity.ID
CreateLamportTime lamport.Time
EditLamportTime lamport.Time
CreateUnixTime int64
EditUnixTime int64
Status bug.Status
Labels []bug.Label
Title string
LenComments int
Actors []entity.ID
Participants []entity.ID
// If author is identity.Bare, LegacyAuthor is set
// If author is identity.Identity, AuthorId is set and data is deported
// in a IdentityExcerpt
LegacyAuthor LegacyAuthorExcerpt
AuthorId string
CreateMetadata map[string]string
}
// identity.Bare data are directly embedded in the bug excerpt
type LegacyAuthorExcerpt struct {
Name string
Login string
}
func (l LegacyAuthorExcerpt) DisplayName() string {
switch {
case l.Name == "" && l.Login != "":
return l.Login
case l.Name != "" && l.Login == "":
return l.Name
case l.Name != "" && l.Login != "":
return fmt.Sprintf("%s (%s)", l.Name, l.Login)
}
panic("invalid person data")
}
func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
participantsIds := make([]string, len(snap.Participants))
for i, participant := range snap.Participants {
participantsIds[i] = participant.Id()
}
actorsIds := make([]string, len(snap.Actors))
for i, actor := range snap.Actors {
actorsIds[i] = actor.Id()
}
e := &BugExcerpt{
Id: b.Id(),
CreateLamportTime: b.CreateLamportTime(),
EditLamportTime: b.EditLamportTime(),
CreateUnixTime: b.FirstOp().GetUnixTime(),
EditUnixTime: snap.LastEditUnix(),
Status: snap.Status,
Labels: snap.Labels,
Actors: actorsIds,
Participants: participantsIds,
Title: snap.Title,
LenComments: len(snap.Comments),
CreateMetadata: b.FirstOp().AllMetadata(),
}
switch snap.Author.(type) {
case *identity.Identity:
e.AuthorId = snap.Author.Id()
case *identity.Bare:
e.LegacyAuthor = LegacyAuthorExcerpt{
Login: snap.Author.Login(),
Name: snap.Author.Name(),
}
default:
panic("unhandled identity type")
}
return e
}
func (b *BugExcerpt) HumanId() string {
return bug.FormatHumanID(b.Id)
}
/*
* Sorting
*/
type BugsById []*BugExcerpt
func (b BugsById) Len() int {
return len(b)
}
func (b BugsById) Less(i, j int) bool {
return b[i].Id < b[j].Id
}
func (b BugsById) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
type BugsByCreationTime []*BugExcerpt
func (b BugsByCreationTime) Len() int {
return len(b)
}
func (b BugsByCreationTime) Less(i, j int) bool {
if b[i].CreateLamportTime < b[j].CreateLamportTime {
return true
}
if b[i].CreateLamportTime > b[j].CreateLamportTime {
return false
}
// When the logical clocks are identical, that means we had a concurrent
// edition. In this case we rely on the timestamp. While the timestamp might
// be incorrect due to a badly set clock, the drift in sorting is bounded
// by the first sorting using the logical clock. That means that if users
// synchronize their bugs regularly, the timestamp will rarely be used, and
// should still provide a kinda accurate sorting when needed.
return b[i].CreateUnixTime < b[j].CreateUnixTime
}
func (b BugsByCreationTime) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
type BugsByEditTime []*BugExcerpt
func (b BugsByEditTime) Len() int {
return len(b)
}
func (b BugsByEditTime) Less(i, j int) bool {
if b[i].EditLamportTime < b[j].EditLamportTime {
return true
}
if b[i].EditLamportTime > b[j].EditLamportTime {
return false
}
// When the logical clocks are identical, that means we had a concurrent
// edition. In this case we rely on the timestamp. While the timestamp might
// be incorrect due to a badly set clock, the drift in sorting is bounded
// by the first sorting using the logical clock. That means that if users
// synchronize their bugs regularly, the timestamp will rarely be used, and
// should still provide a kinda accurate sorting when needed.
return b[i].EditUnixTime < b[j].EditUnixTime
}
func (b BugsByEditTime) Swap(i, j int) {
b[i], b[j] = b[j], b[i]
}
|