aerc/worker/lib/sort.go
Reto Brunner fc9ccc3000 remove models.Address in favor of go-message Address
We made a new type out of go-message/mail.Address without any real reason.
This suddenly made it necessary to convert from one to the other without actually
having any benefit whatsoever.
This commit gets rid of the additional type
2020-11-14 15:40:13 +01:00

153 lines
4.1 KiB
Go

package lib
import (
"sort"
"strings"
"git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/worker/types"
"github.com/emersion/go-message/mail"
)
func Sort(messageInfos []*models.MessageInfo,
criteria []*types.SortCriterion) ([]uint32, error) {
// loop through in reverse to ensure we sort by non-primary fields first
for i := len(criteria) - 1; i >= 0; i-- {
criterion := criteria[i]
switch criterion.Field {
case types.SortArrival:
sortSlice(criterion, messageInfos, func(i, j int) bool {
return messageInfos[i].InternalDate.Before(messageInfos[j].InternalDate)
})
case types.SortCc:
sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*mail.Address {
return msgInfo.Envelope.Cc
})
case types.SortDate:
sortSlice(criterion, messageInfos, func(i, j int) bool {
return messageInfos[i].Envelope.Date.Before(messageInfos[j].Envelope.Date)
})
case types.SortFrom:
sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*mail.Address {
return msgInfo.Envelope.From
})
case types.SortRead:
sortFlags(messageInfos, criterion, models.SeenFlag)
case types.SortSize:
sortSlice(criterion, messageInfos, func(i, j int) bool {
return messageInfos[i].Size < messageInfos[j].Size
})
case types.SortSubject:
sortStrings(messageInfos, criterion,
func(msgInfo *models.MessageInfo) string {
subject := strings.ToLower(msgInfo.Envelope.Subject)
subject = strings.TrimPrefix(subject, "re: ")
return strings.TrimPrefix(subject, "fwd: ")
})
case types.SortTo:
sortAddresses(messageInfos, criterion,
func(msgInfo *models.MessageInfo) []*mail.Address {
return msgInfo.Envelope.To
})
}
}
var uids []uint32
// copy in reverse as msgList displays backwards
for i := len(messageInfos) - 1; i >= 0; i-- {
uids = append(uids, messageInfos[i].Uid)
}
return uids, nil
}
func sortAddresses(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) []*mail.Address) {
sortSlice(criterion, messageInfos, func(i, j int) bool {
addressI, addressJ := getValue(messageInfos[i]), getValue(messageInfos[j])
var firstI, firstJ *mail.Address
if len(addressI) > 0 {
firstI = addressI[0]
}
if len(addressJ) > 0 {
firstJ = addressJ[0]
}
if firstI == nil && firstJ == nil {
return false
} else if firstI == nil && firstJ != nil {
return false
} else if firstI != nil && firstJ == nil {
return true
} else /* firstI != nil && firstJ != nil */ {
getName := func(addr *mail.Address) string {
if addr.Name != "" {
return addr.Name
} else {
return addr.Address
}
}
return getName(firstI) < getName(firstJ)
}
})
}
func sortFlags(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
testFlag models.Flag) {
var slice []*boolStore
for _, msgInfo := range messageInfos {
flagPresent := false
for _, flag := range msgInfo.Flags {
if flag == testFlag {
flagPresent = true
}
}
slice = append(slice, &boolStore{
Value: flagPresent,
MsgInfo: msgInfo,
})
}
sortSlice(criterion, slice, func(i, j int) bool {
valI, valJ := slice[i].Value, slice[j].Value
return valI && !valJ
})
for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo
}
}
func sortStrings(messageInfos []*models.MessageInfo, criterion *types.SortCriterion,
getValue func(*models.MessageInfo) string) {
var slice []*lexiStore
for _, msgInfo := range messageInfos {
slice = append(slice, &lexiStore{
Value: getValue(msgInfo),
MsgInfo: msgInfo,
})
}
sortSlice(criterion, slice, func(i, j int) bool {
return slice[i].Value < slice[j].Value
})
for i := 0; i < len(messageInfos); i++ {
messageInfos[i] = slice[i].MsgInfo
}
}
type lexiStore struct {
Value string
MsgInfo *models.MessageInfo
}
type boolStore struct {
Value bool
MsgInfo *models.MessageInfo
}
func sortSlice(criterion *types.SortCriterion, slice interface{}, less func(i, j int) bool) {
if criterion.Reverse {
sort.SliceStable(slice, func(i, j int) bool {
return less(j, i)
})
} else {
sort.SliceStable(slice, less)
}
}