1package cache
  2
  3import (
  4	"encoding/gob"
  5	"fmt"
  6	"time"
  7
  8	"github.com/MichaelMure/git-bug/entities/bug"
  9	"github.com/MichaelMure/git-bug/entities/common"
 10	"github.com/MichaelMure/git-bug/entities/identity"
 11	"github.com/MichaelMure/git-bug/entity"
 12	"github.com/MichaelMure/git-bug/util/lamport"
 13)
 14
 15// Package initialisation used to register the type for (de)serialization
 16func init() {
 17	gob.Register(BugExcerpt{})
 18}
 19
 20// BugExcerpt hold a subset of the bug values to be able to sort and filter bugs
 21// efficiently without having to read and compile each raw bugs.
 22type BugExcerpt struct {
 23	Id entity.Id
 24
 25	CreateLamportTime lamport.Time
 26	EditLamportTime   lamport.Time
 27	CreateUnixTime    int64
 28	EditUnixTime      int64
 29
 30	AuthorId     entity.Id
 31	Status       common.Status
 32	Labels       []bug.Label
 33	Title        string
 34	LenComments  int
 35	Actors       []entity.Id
 36	Participants []entity.Id
 37
 38	CreateMetadata map[string]string
 39}
 40
 41// identity.Bare data are directly embedded in the bug excerpt
 42type LegacyAuthorExcerpt struct {
 43	Name  string
 44	Login string
 45}
 46
 47func (l LegacyAuthorExcerpt) DisplayName() string {
 48	switch {
 49	case l.Name == "" && l.Login != "":
 50		return l.Login
 51	case l.Name != "" && l.Login == "":
 52		return l.Name
 53	case l.Name != "" && l.Login != "":
 54		return fmt.Sprintf("%s (%s)", l.Name, l.Login)
 55	}
 56
 57	panic("invalid person data")
 58}
 59
 60func NewBugExcerpt(b bug.Interface, snap *bug.Snapshot) *BugExcerpt {
 61	participantsIds := make([]entity.Id, 0, len(snap.Participants))
 62	for _, participant := range snap.Participants {
 63		participantsIds = append(participantsIds, participant.Id())
 64	}
 65
 66	actorsIds := make([]entity.Id, 0, len(snap.Actors))
 67	for _, actor := range snap.Actors {
 68		actorsIds = append(actorsIds, actor.Id())
 69	}
 70
 71	e := &BugExcerpt{
 72		Id:                b.Id(),
 73		CreateLamportTime: b.CreateLamportTime(),
 74		EditLamportTime:   b.EditLamportTime(),
 75		CreateUnixTime:    b.FirstOp().Time().Unix(),
 76		EditUnixTime:      snap.EditTime().Unix(),
 77		Status:            snap.Status,
 78		Labels:            snap.Labels,
 79		Actors:            actorsIds,
 80		Participants:      participantsIds,
 81		Title:             snap.Title,
 82		LenComments:       len(snap.Comments),
 83		CreateMetadata:    b.FirstOp().AllMetadata(),
 84	}
 85
 86	switch snap.Author.(type) {
 87	case *identity.Identity, *identity.IdentityStub, *IdentityCache:
 88		e.AuthorId = snap.Author.Id()
 89	default:
 90		panic("unhandled identity type")
 91	}
 92
 93	return e
 94}
 95
 96func (b *BugExcerpt) CreateTime() time.Time {
 97	return time.Unix(b.CreateUnixTime, 0)
 98}
 99
100func (b *BugExcerpt) EditTime() time.Time {
101	return time.Unix(b.EditUnixTime, 0)
102}
103
104/*
105 * Sorting
106 */
107
108type BugsById []*BugExcerpt
109
110func (b BugsById) Len() int {
111	return len(b)
112}
113
114func (b BugsById) Less(i, j int) bool {
115	return b[i].Id < b[j].Id
116}
117
118func (b BugsById) Swap(i, j int) {
119	b[i], b[j] = b[j], b[i]
120}
121
122type BugsByCreationTime []*BugExcerpt
123
124func (b BugsByCreationTime) Len() int {
125	return len(b)
126}
127
128func (b BugsByCreationTime) Less(i, j int) bool {
129	if b[i].CreateLamportTime < b[j].CreateLamportTime {
130		return true
131	}
132
133	if b[i].CreateLamportTime > b[j].CreateLamportTime {
134		return false
135	}
136
137	// When the logical clocks are identical, that means we had a concurrent
138	// edition. In this case we rely on the timestamp. While the timestamp might
139	// be incorrect due to a badly set clock, the drift in sorting is bounded
140	// by the first sorting using the logical clock. That means that if users
141	// synchronize their bugs regularly, the timestamp will rarely be used, and
142	// should still provide a kinda accurate sorting when needed.
143	return b[i].CreateUnixTime < b[j].CreateUnixTime
144}
145
146func (b BugsByCreationTime) Swap(i, j int) {
147	b[i], b[j] = b[j], b[i]
148}
149
150type BugsByEditTime []*BugExcerpt
151
152func (b BugsByEditTime) Len() int {
153	return len(b)
154}
155
156func (b BugsByEditTime) Less(i, j int) bool {
157	if b[i].EditLamportTime < b[j].EditLamportTime {
158		return true
159	}
160
161	if b[i].EditLamportTime > b[j].EditLamportTime {
162		return false
163	}
164
165	// When the logical clocks are identical, that means we had a concurrent
166	// edition. In this case we rely on the timestamp. While the timestamp might
167	// be incorrect due to a badly set clock, the drift in sorting is bounded
168	// by the first sorting using the logical clock. That means that if users
169	// synchronize their bugs regularly, the timestamp will rarely be used, and
170	// should still provide a kinda accurate sorting when needed.
171	return b[i].EditUnixTime < b[j].EditUnixTime
172}
173
174func (b BugsByEditTime) Swap(i, j int) {
175	b[i], b[j] = b[j], b[i]
176}