aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/account.go4
-rw-r--r--app/dirtree.go213
-rw-r--r--app/msglist.go18
-rw-r--r--app/msgviewer.go2
-rw-r--r--app/providesmessage.go2
-rw-r--r--commands/account/clear.go2
-rw-r--r--commands/account/export-mbox.go19
-rw-r--r--commands/account/next.go2
-rw-r--r--commands/account/search.go2
-rw-r--r--commands/msg/archive.go10
-rw-r--r--commands/msg/copy.go2
-rw-r--r--commands/msg/delete.go4
-rw-r--r--commands/msg/forward.go6
-rw-r--r--commands/msg/invite.go2
-rw-r--r--commands/msg/mark.go5
-rw-r--r--commands/msg/move.go4
-rw-r--r--commands/msg/pipe.go3
-rw-r--r--commands/msg/read.go4
-rw-r--r--commands/msg/recall.go2
-rw-r--r--commands/msg/reply.go4
-rw-r--r--commands/msg/utils.go2
-rw-r--r--commands/util.go16
-rw-r--r--lib/emlview.go4
-rw-r--r--lib/iterator/impl.go9
-rw-r--r--lib/iterator/iterator_test.go17
-rw-r--r--lib/marker/marker.go48
-rw-r--r--lib/marker/marker_test.go27
-rw-r--r--lib/messageview.go4
-rw-r--r--lib/msgstore.go99
-rw-r--r--lib/rfc822/message.go2
-rw-r--r--lib/rfc822/message_test.go2
-rw-r--r--lib/sort/sort.go5
-rw-r--r--lib/threadbuilder.go42
-rw-r--r--lib/uidstore/uidstore.go62
-rw-r--r--models/models.go34
-rw-r--r--worker/imap/cache.go12
-rw-r--r--worker/imap/extensions/xgmext/client.go21
-rw-r--r--worker/imap/fetch.go22
-rw-r--r--worker/imap/flags.go6
-rw-r--r--worker/imap/imap.go8
-rw-r--r--worker/imap/list.go2
-rw-r--r--worker/imap/open.go8
-rw-r--r--worker/imap/worker.go4
-rw-r--r--worker/jmap/directories.go8
-rw-r--r--worker/jmap/fetch.go22
-rw-r--r--worker/jmap/jmap.go2
-rw-r--r--worker/jmap/push.go5
-rw-r--r--worker/jmap/set.go26
-rw-r--r--worker/jmap/worker.go3
-rw-r--r--worker/lib/search.go4
-rw-r--r--worker/lib/sort.go4
-rw-r--r--worker/maildir/container.go73
-rw-r--r--worker/maildir/message.go4
-rw-r--r--worker/maildir/search.go9
-rw-r--r--worker/maildir/worker.go16
-rw-r--r--worker/mbox/io.go7
-rw-r--r--worker/mbox/models.go42
-rw-r--r--worker/mbox/worker.go6
-rw-r--r--worker/notmuch/lib/database.go14
-rw-r--r--worker/notmuch/message.go4
-rw-r--r--worker/notmuch/worker.go28
-rw-r--r--worker/types/messages.go32
-rw-r--r--worker/types/thread.go29
-rw-r--r--worker/types/thread_test.go86
64 files changed, 541 insertions, 649 deletions
diff --git a/app/account.go b/app/account.go
index 5577e461..6d690142 100644
--- a/app/account.go
+++ b/app/account.go
@@ -240,7 +240,7 @@ func (acct *AccountView) SelectedMessage() (*models.MessageInfo, error) {
return msg, nil
}
-func (acct *AccountView) MarkedMessages() ([]uint32, error) {
+func (acct *AccountView) MarkedMessages() ([]models.UID, error) {
if store := acct.Store(); store != nil {
return store.Marker().Marked(), nil
}
@@ -495,7 +495,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
acct.setTitle()
}
-func (acct *AccountView) updateDirCounts(destination string, uids []uint32) {
+func (acct *AccountView) updateDirCounts(destination string, uids []models.UID) {
// Only update the destination destDir if it is initialized
if destDir := acct.dirlist.Directory(destination); destDir != nil {
var recent, unseen int
diff --git a/app/dirtree.go b/app/dirtree.go
index a7d0a5e7..dc80fa7f 100644
--- a/app/dirtree.go
+++ b/app/dirtree.go
@@ -3,7 +3,6 @@ package app
import (
"fmt"
"sort"
- "strconv"
"strings"
"time"
@@ -23,8 +22,6 @@ type DirectoryTree struct {
listIdx int
list []*types.Thread
- treeDirs []string
-
virtual bool
virtualCb func()
}
@@ -33,7 +30,6 @@ func NewDirectoryTree(dirlist *DirectoryList) DirectoryLister {
dt := &DirectoryTree{
DirectoryList: dirlist,
listIdx: -1,
- list: make([]*types.Thread, 0),
virtualCb: func() {},
}
return dt
@@ -48,13 +44,12 @@ func (dt *DirectoryTree) Selected() string {
return dt.DirectoryList.Selected()
}
node := dt.list[dt.listIdx]
- sep := dt.DirectoryList.worker.PathSeparator()
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], sep)
+ elems := dt.nodeElems(node)
n := countLevels(node)
if n < 0 || n >= len(elems) {
return ""
}
- return strings.Join(elems[:(n+1)], sep)
+ return strings.Join(elems[:(n+1)], dt.DirectoryList.worker.PathSeparator())
}
func (dt *DirectoryTree) SelectedDirectory() *models.Directory {
@@ -211,27 +206,24 @@ func (dt *DirectoryTree) SelectedMsgStore() (*lib.MessageStore, bool) {
if dt.virtual {
return nil, false
}
- if findString(dt.treeDirs, dt.selected) < 0 {
+
+ selected := models.UID(dt.selected)
+ if _, node := dt.getTreeNode(selected); node == nil {
dt.buildTree()
- if idx := findString(dt.treeDirs, dt.selected); idx >= 0 {
- selIdx, node := dt.getTreeNode(uint32(idx))
- if node != nil {
- makeVisible(node)
- dt.listIdx = selIdx
- }
+ selIdx, node := dt.getTreeNode(selected)
+ if node != nil {
+ makeVisible(node)
+ dt.listIdx = selIdx
}
}
return dt.DirectoryList.SelectedMsgStore()
}
func (dt *DirectoryTree) reindex(name string) {
- idx := findString(dt.treeDirs, name)
- if idx >= 0 {
- selIdx, node := dt.getTreeNode(uint32(idx))
- if node != nil {
- makeVisible(node)
- dt.listIdx = selIdx
- }
+ selIdx, node := dt.getTreeNode(models.UID(name))
+ if node != nil {
+ makeVisible(node)
+ dt.listIdx = selIdx
}
}
@@ -247,7 +239,8 @@ func (dt *DirectoryTree) Open(name string, query string, delay time.Duration, cb
return
}
again := false
- if findString(dt.dirs, name) < 0 {
+ uid := models.UID(name)
+ if _, node := dt.getTreeNode(uid); node == nil {
again = true
} else {
dt.reindex(name)
@@ -300,13 +293,14 @@ func (dt *DirectoryTree) NextPrev(delta int) {
func (dt *DirectoryTree) selectIndex(i int) {
dt.listIdx = i
- if path := dt.getDirectory(dt.list[dt.listIdx]); path != "" {
- dt.virtual = false
- dt.Select(path)
- } else {
+ node := dt.list[dt.listIdx]
+ if node.Dummy {
dt.virtual = true
dt.NewContext()
dt.virtualCb()
+ } else {
+ dt.virtual = false
+ dt.Select(dt.getDirectory(node))
}
}
@@ -345,37 +339,43 @@ func (dt *DirectoryTree) countVisible(list []*types.Thread) (n int) {
return
}
+func (dt *DirectoryTree) nodeElems(node *types.Thread) []string {
+ dir := string(node.Uid)
+ sep := dt.DirectoryList.worker.PathSeparator()
+ return strings.Split(dir, sep)
+}
+
+func (dt *DirectoryTree) nodeName(node *types.Thread) string {
+ if elems := dt.nodeElems(node); len(elems) > 0 {
+ return elems[len(elems)-1]
+ }
+ return ""
+}
+
func (dt *DirectoryTree) displayText(node *types.Thread) string {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
return fmt.Sprintf("%s%s%s",
threadPrefix(node, false, false),
- getFlag(node), elems[countLevels(node)])
+ getFlag(node), dt.nodeName(node))
}
func (dt *DirectoryTree) getDirectory(node *types.Thread) string {
- if uid := node.Uid; int(uid) < len(dt.treeDirs) {
- return dt.treeDirs[uid]
- }
- return ""
+ return string(node.Uid)
}
-func (dt *DirectoryTree) getTreeNode(uid uint32) (int, *types.Thread) {
- var found *types.Thread
- var idx int
+func (dt *DirectoryTree) getTreeNode(uid models.UID) (int, *types.Thread) {
for i, node := range dt.list {
if node.Uid == uid {
- found = node
- idx = i
+ return i, node
}
}
- return idx, found
+ return -1, nil
}
func (dt *DirectoryTree) hiddenDirectories() map[string]bool {
hidden := make(map[string]bool, 0)
for _, node := range dt.list {
if node.Hidden != 0 && node.FirstChild != nil {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
+ elems := dt.nodeElems(node)
if levels := countLevels(node); levels < len(elems) {
if node.FirstChild != nil && (levels+1) < len(elems) {
levels += 1
@@ -390,8 +390,9 @@ func (dt *DirectoryTree) hiddenDirectories() map[string]bool {
}
func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
+ log.Tracef("setHiddenDirectories: %#v", hiddenDirs)
for _, node := range dt.list {
- elems := strings.Split(dt.treeDirs[getAnyUid(node)], dt.DirectoryList.worker.PathSeparator())
+ elems := dt.nodeElems(node)
if levels := countLevels(node); levels < len(elems) {
if node.FirstChild != nil && (levels+1) < len(elems) {
levels += 1
@@ -399,6 +400,7 @@ func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
strDir := strings.Join(elems[:levels], dt.DirectoryList.worker.PathSeparator())
if hidden, ok := hiddenDirs[strDir]; hidden && ok {
node.Hidden = 1
+ log.Tracef("setHiddenDirectories: %q -> %#v", strDir, node)
}
}
}
@@ -407,29 +409,15 @@ func (dt *DirectoryTree) setHiddenDirectories(hiddenDirs map[string]bool) {
func (dt *DirectoryTree) buildTree() {
if len(dt.list) != 0 {
hiddenDirs := dt.hiddenDirectories()
- defer func() {
- dt.setHiddenDirectories(hiddenDirs)
- }()
- }
-
- sTree := make([][]string, 0)
- for i, dir := range dt.dirs {
- elems := strings.Split(dir, dt.DirectoryList.worker.PathSeparator())
- if len(elems) == 0 {
- continue
- }
- elems = append(elems, fmt.Sprintf("%d", i))
- sTree = append(sTree, elems)
+ defer dt.setHiddenDirectories(hiddenDirs)
}
- dt.treeDirs = make([]string, len(dt.dirs))
- copy(dt.treeDirs, dt.dirs)
-
- root := &types.Thread{Uid: 0}
- dt.buildTreeNode(root, sTree, 0xFFFFFF, 1)
-
- threads := make([]*types.Thread, 0)
+ dirs := make([]string, len(dt.dirs))
+ copy(dirs, dt.dirs)
+ root := &types.Thread{}
+ dt.buildTreeNode(root, dirs, 1)
+ var threads []*types.Thread
for iter := root.FirstChild; iter != nil; iter = iter.NextSibling {
iter.Parent = nil
threads = append(threads, iter)
@@ -437,16 +425,10 @@ func (dt *DirectoryTree) buildTree() {
// folders-sort
if dt.DirectoryList.acctConf.EnableFoldersSort {
- toStr := func(t *types.Thread) string {
- if elems := strings.Split(dt.treeDirs[getAnyUid(t)], dt.DirectoryList.worker.PathSeparator()); len(elems) > 0 {
- return elems[0]
- }
- return ""
- }
sort.Slice(threads, func(i, j int) bool {
foldersSort := dt.DirectoryList.acctConf.FoldersSort
- iInFoldersSort := findString(foldersSort, toStr(threads[i]))
- jInFoldersSort := findString(foldersSort, toStr(threads[j]))
+ iInFoldersSort := findString(foldersSort, dt.getDirectory(threads[i]))
+ jInFoldersSort := findString(foldersSort, dt.getDirectory(threads[j]))
if iInFoldersSort >= 0 && jInFoldersSort >= 0 {
return iInFoldersSort < jInFoldersSort
}
@@ -456,7 +438,7 @@ func (dt *DirectoryTree) buildTree() {
if jInFoldersSort >= 0 {
return false
}
- return toStr(threads[i]) < toStr(threads[j])
+ return dt.getDirectory(threads[i]) < dt.getDirectory(threads[j])
})
}
@@ -472,41 +454,51 @@ func (dt *DirectoryTree) buildTree() {
}
}
-func (dt *DirectoryTree) buildTreeNode(node *types.Thread, stree [][]string, defaultUid uint32, depth int) {
- m := make(map[string][][]string)
- for _, branch := range stree {
- if len(branch) > 1 {
- next := append(m[branch[0]], branch[1:]) //nolint:gocritic // intentional append to different slice
- m[branch[0]] = next
- }
- }
- keys := make([]string, 0)
- for key := range m {
- keys = append(keys, key)
- }
- sort.Strings(keys)
- path := dt.getDirectory(node)
- for _, key := range keys {
- next := m[key]
- var uid uint32 = defaultUid
- for _, testStr := range next {
- if len(testStr) == 1 {
- if uidI, err := strconv.Atoi(next[0][0]); err == nil {
- uid = uint32(uidI)
- }
+func (dt *DirectoryTree) buildTreeNode(node *types.Thread, dirs []string, depth int) {
+ dirmap := make(map[string][]string)
+ for _, dir := range dirs {
+ base, dir, cut := strings.Cut(
+ dir, dt.DirectoryList.worker.PathSeparator())
+ if _, found := dirmap[base]; found {
+ if cut {
+ dirmap[base] = append(dirmap[base], dir)
}
+ } else if cut {
+ dirmap[base] = append(dirmap[base], dir)
+ } else {
+ dirmap[base] = []string{}
}
- nextNode := &types.Thread{Uid: uid}
+ }
+ bases := make([]string, 0, len(dirmap))
+ for base, dirs := range dirmap {
+ bases = append(bases, base)
+ sort.Strings(dirs)
+ }
+ sort.Strings(bases)
+
+ basePath := dt.getDirectory(node)
+ if depth > dt.UiConfig(basePath).DirListCollapse {
+ node.Hidden = 1
+ } else {
+ node.Hidden = 0
+ }
+
+ for _, base := range bases {
+ path := dt.childPath(basePath, base)
+ nextNode := &types.Thread{Uid: models.UID(path)}
+
+ nextNode.Dummy = findString(dt.dirs, path) == -1
+
node.AddChild(nextNode)
- if dt.UiConfig(path).DirListCollapse != 0 && dt.listIdx < 0 {
- if depth > dt.UiConfig(path).DirListCollapse {
- node.Hidden = 1
- } else {
- node.Hidden = 0
- }
- }
- dt.buildTreeNode(nextNode, next, defaultUid, depth+1)
+ dt.buildTreeNode(nextNode, dirmap[base], depth+1)
+ }
+}
+
+func (dt *DirectoryTree) childPath(base, relpath string) string {
+ if base == "" {
+ return relpath
}
+ return base + dt.DirectoryList.worker.PathSeparator() + relpath
}
func makeVisible(node *types.Thread) {
@@ -519,27 +511,12 @@ func makeVisible(node *types.Thread) {
}
func isVisible(node *types.Thread) bool {
- isVisible := true
for iter := node.Parent; iter != nil; iter = iter.Parent {
if iter.Hidden != 0 {
- isVisible = false
- break
+ return false
}
}
- return isVisible
-}
-
-func getAnyUid(node *types.Thread) (uid uint32) {
- err := node.Walk(func(t *types.Thread, l int, err error) error {
- if t.FirstChild == nil {
- uid = t.Uid
- }
- return nil
- })
- if err != nil {
- log.Warnf("failed to get uid: %v", err)
- }
- return
+ return true
}
func countLevels(node *types.Thread) (level int) {
@@ -550,7 +527,7 @@ func countLevels(node *types.Thread) (level int) {
}
func getFlag(node *types.Thread) string {
- if node == nil && node.FirstChild == nil {
+ if node == nil || node.FirstChild == nil {
return ""
}
if node.Hidden != 0 {
diff --git a/app/msglist.go b/app/msglist.go
index 9e6f3289..4db54f4d 100644
--- a/app/msglist.go
+++ b/app/msglist.go
@@ -44,7 +44,7 @@ func (ml *MessageList) Invalidate() {
}
type messageRowParams struct {
- uid uint32
+ uid models.UID
needsHeaders bool
err error
uiConfig *config.UIConfig
@@ -61,7 +61,7 @@ func (ml *MessageList) AlignMessage(pos AlignPosition) {
idx := 0
iter := store.UidsIterator()
for i := 0; iter.Next(); i++ {
- if store.SelectedUid() == iter.Value().(uint32) {
+ if store.SelectedUid() == iter.Value().(models.UID) {
idx = i
break
}
@@ -92,7 +92,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
ml.UpdateScroller(ml.height, len(store.Uids()))
iter := store.UidsIterator()
for i := 0; iter.Next(); i++ {
- if store.SelectedUid() == iter.Value().(uint32) {
+ if store.SelectedUid() == iter.Value().(models.UID) {
ml.EnsureScroll(i)
break
}
@@ -108,7 +108,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
return
}
- var needsHeaders []uint32
+ var needsHeaders []models.UID
data := state.NewDataSetter()
data.SetAccount(acct.acct)
@@ -166,7 +166,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
if i < ml.Scroll() {
continue
}
- uid := iter.Value().(uint32)
+ uid := iter.Value().(models.UID)
if showThreads {
threadView.Update(data, uid)
}
@@ -201,7 +201,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
}
func addMessage(
- store *lib.MessageStore, uid uint32,
+ store *lib.MessageStore, uid models.UID,
table *ui.Table, data state.DataSetter,
uiConfig *config.UIConfig,
) bool {
@@ -406,14 +406,14 @@ func (ml *MessageList) Select(index int) {
iter := store.UidsIterator()
- var uid uint32
+ var uid models.UID
if index < 0 {
uid = uids[iter.EndIndex()]
} else {
uid = uids[iter.StartIndex()]
for i := 0; iter.Next(); i++ {
if i >= index {
- uid = iter.Value().(uint32)
+ uid = iter.Value().(models.UID)
break
}
}
@@ -579,7 +579,7 @@ func newThreadView(store *lib.MessageStore) *threadView {
}
}
-func (t *threadView) Update(data state.DataSetter, uid uint32) {
+func (t *threadView) Update(data state.DataSetter, uid models.UID) {
thread, err := t.store.Thread(uid)
info := state.ThreadInfo{}
if thread != nil && err == nil {
diff --git a/app/msgviewer.go b/app/msgviewer.go
index 8760bf74..d4dbd73e 100644
--- a/app/msgviewer.go
+++ b/app/msgviewer.go
@@ -333,7 +333,7 @@ func (mv *MessageViewer) SelectedMessage() (*models.MessageInfo, error) {
return mv.msg.MessageInfo(), nil
}
-func (mv *MessageViewer) MarkedMessages() ([]uint32, error) {
+func (mv *MessageViewer) MarkedMessages() ([]models.UID, error) {
return mv.acct.MarkedMessages()
}
diff --git a/app/providesmessage.go b/app/providesmessage.go
index c89c811f..4572f65b 100644
--- a/app/providesmessage.go
+++ b/app/providesmessage.go
@@ -26,5 +26,5 @@ type ProvidesMessages interface {
Store() *lib.MessageStore
SelectedAccount() *AccountView
SelectedMessage() (*models.MessageInfo, error)
- MarkedMessages() ([]uint32, error)
+ MarkedMessages() ([]models.UID, error)
}
diff --git a/commands/account/clear.go b/commands/account/clear.go
index 1c13ddf5..ec033c46 100644
--- a/commands/account/clear.go
+++ b/commands/account/clear.go
@@ -35,7 +35,7 @@ func (c Clear) Execute(args []string) error {
}
if c.Selected {
- defer store.Select(0)
+ defer store.Select("")
}
store.ApplyClear()
acct.SetStatus(state.SearchFilterClear())
diff --git a/commands/account/export-mbox.go b/commands/account/export-mbox.go
index 6422eae0..619c24a2 100644
--- a/commands/account/export-mbox.go
+++ b/commands/account/export-mbox.go
@@ -13,6 +13,7 @@ import (
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/lib/xdg"
+ "git.sr.ht/~rjarry/aerc/models"
mboxer "git.sr.ht/~rjarry/aerc/worker/mbox"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -61,7 +62,7 @@ func (e ExportMbox) Execute(args []string) error {
app.PushStatus("Exporting to "+e.Filename, 10*time.Second)
// uids of messages to export
- var uids []uint32
+ var uids []models.UID
// check if something is marked - we export that then
msgProvider, ok := app.SelectedTabContent().(app.ProvidesMessages)
@@ -98,7 +99,7 @@ func (e ExportMbox) Execute(args []string) error {
defer file.Close()
var mu sync.Mutex
- var ctr uint32
+ var ctr uint
var retries int
done := make(chan bool)
@@ -159,15 +160,15 @@ func (e ExportMbox) Execute(args []string) error {
return nil
}
-func sortMarkedUids(marked []uint32, store *lib.MessageStore) ([]uint32, error) {
- lookup := map[uint32]bool{}
+func sortMarkedUids(marked []models.UID, store *lib.MessageStore) ([]models.UID, error) {
+ lookup := map[models.UID]bool{}
for _, uid := range marked {
lookup[uid] = true
}
- uids := []uint32{}
+ uids := []models.UID{}
iter := store.UidsIterator()
for iter.Next() {
- uid, ok := iter.Value().(uint32)
+ uid, ok := iter.Value().(models.UID)
if !ok {
return nil, errors.New("Invalid message UID value")
}
@@ -179,11 +180,11 @@ func sortMarkedUids(marked []uint32, store *lib.MessageStore) ([]uint32, error)
return uids, nil
}
-func sortAllUids(store *lib.MessageStore) ([]uint32, error) {
- uids := []uint32{}
+func sortAllUids(store *lib.MessageStore) ([]models.UID, error) {
+ uids := []models.UID{}
iter := store.UidsIterator()
for iter.Next() {
- uid, ok := iter.Value().(uint32)
+ uid, ok := iter.Value().(models.UID)
if !ok {
return nil, errors.New("Invalid message UID value")
}
diff --git a/commands/account/next.go b/commands/account/next.go
index e14b14fb..b54ed0c1 100644
--- a/commands/account/next.go
+++ b/commands/account/next.go
@@ -85,7 +85,7 @@ func (np NextPrevMsg) Execute(args []string) error {
if nextMsg := store.Selected(); nextMsg != nil {
reloadViewer(nextMsg)
} else {
- store.FetchHeaders([]uint32{store.SelectedUid()},
+ store.FetchHeaders([]models.UID{store.SelectedUid()},
func(msg types.WorkerMessage) {
if m, ok := msg.(*types.MessageInfo); ok {
reloadViewer(m.Info)
diff --git a/commands/account/search.go b/commands/account/search.go
index 5c2eaec7..14aa367b 100644
--- a/commands/account/search.go
+++ b/commands/account/search.go
@@ -191,7 +191,7 @@ func (s SearchFilter) Execute(args []string) error {
store.Sort(store.GetCurrentSortCriteria(), cb)
} else {
acct.SetStatus(state.Search("Searching..."))
- cb := func(uids []uint32) {
+ cb := func(uids []models.UID) {
acct.SetStatus(state.Search(strings.Join(args, " ")))
log.Tracef("Search results: %v", uids)
store.ApplySearch(uids)
diff --git a/commands/msg/archive.go b/commands/msg/archive.go
index 8c5f12b9..c262de41 100644
--- a/commands/msg/archive.go
+++ b/commands/msg/archive.go
@@ -89,7 +89,7 @@ func archive(msgs []*models.MessageInfo, mfs *types.MultiFileStrategy,
if err != nil {
return err
}
- var uids []uint32
+ var uids []models.UID
for _, msg := range msgs {
uids = append(uids, msg.Uid)
}
@@ -98,7 +98,7 @@ func archive(msgs []*models.MessageInfo, mfs *types.MultiFileStrategy,
marker.ClearVisualMark()
next := findNextNonDeleted(uids, store)
- var uidMap map[string][]uint32
+ var uidMap map[string][]models.UID
switch archiveType {
case ARCHIVE_MONTH:
uidMap = groupBy(msgs, func(msg *models.MessageInfo) string {
@@ -120,7 +120,7 @@ func archive(msgs []*models.MessageInfo, mfs *types.MultiFileStrategy,
return dir
})
case ARCHIVE_FLAT:
- uidMap = make(map[string][]uint32)
+ uidMap = make(map[string][]models.UID)
uidMap[archiveDir] = commands.UidsFromMessageInfos(msgs)
}
@@ -164,8 +164,8 @@ func archive(msgs []*models.MessageInfo, mfs *types.MultiFileStrategy,
func groupBy(msgs []*models.MessageInfo,
grouper func(*models.MessageInfo) string,
-) map[string][]uint32 {
- m := make(map[string][]uint32)
+) map[string][]models.UID {
+ m := make(map[string][]models.UID)
for _, msg := range msgs {
group := grouper(msg)
m[group] = append(m[group], msg.Uid)
diff --git a/commands/msg/copy.go b/commands/msg/copy.go
index d9c40ee2..52a6ea6c 100644
--- a/commands/msg/copy.go
+++ b/commands/msg/copy.go
@@ -175,7 +175,7 @@ func (c Copy) Execute(args []string) error {
return nil
}
-func (c Copy) CallBack(msg types.WorkerMessage, uids []uint32, store *lib.MessageStore) {
+func (c Copy) CallBack(msg types.WorkerMessage, uids []models.UID, store *lib.MessageStore) {
dest := c.Folder
if len(c.Account) != 0 {
dest = fmt.Sprintf("%s in %s", c.Folder, c.Account)
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index 0d269eab..d58b14ad 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -117,7 +117,7 @@ func (d Delete) Execute(args []string) error {
return nil
}
-func findNextNonDeleted(deleted []uint32, store *lib.MessageStore) *models.MessageInfo {
+func findNextNonDeleted(deleted []models.UID, store *lib.MessageStore) *models.MessageInfo {
var next, previous *models.MessageInfo
stepper := []func(){store.Next, store.Prev}
for _, stepFn := range stepper {
@@ -146,7 +146,7 @@ func findNextNonDeleted(deleted []uint32, store *lib.MessageStore) *models.Messa
return next
}
-func contains(uids []uint32, uid uint32) bool {
+func contains(uids []models.UID, uid models.UID) bool {
for _, item := range uids {
if item == uid {
return true
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index 7c491a06..d030636e 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -131,7 +131,7 @@ func (f forward) Execute(args []string) error {
fetchFull = mv.MessageView().FetchFull
} else {
fetchFull = func(cb func(io.Reader)) {
- store.FetchFull([]uint32{msg.Uid}, func(fm *types.FullMessage) {
+ store.FetchFull([]models.UID{msg.Uid}, func(fm *types.FullMessage) {
if fm == nil || (fm != nil && fm.Content == nil) {
return
}
@@ -164,7 +164,7 @@ func (f forward) Execute(args []string) error {
composer.AddAttachment(tmpFileName)
composer.OnClose(func(c *app.Composer) {
if c.Sent() {
- store.Forwarded([]uint32{msg.Uid}, true, nil)
+ store.Forwarded([]models.UID{msg.Uid}, true, nil)
}
os.RemoveAll(tmpDir)
})
@@ -216,7 +216,7 @@ func (f forward) Execute(args []string) error {
composer.OnClose(func(c *app.Composer) {
if c.Sent() {
- store.Forwarded([]uint32{msg.Uid}, true, nil)
+ store.Forwarded([]models.UID{msg.Uid}, true, nil)
}
})
diff --git a/commands/msg/invite.go b/commands/msg/invite.go
index c62397c5..63ad2d3a 100644
--- a/commands/msg/invite.go
+++ b/commands/msg/invite.go
@@ -141,7 +141,7 @@ func (i invite) Execute(args []string) error {
composer.OnClose(func(c *app.Composer) {
if c.Sent() {
- store.Answered([]uint32{msg.Uid}, true, nil)
+ store.Answered([]models.UID{msg.Uid}, true, nil)
}
})
diff --git a/commands/msg/mark.go b/commands/msg/mark.go
index 5717e33d..42110038 100644
--- a/commands/msg/mark.go
+++ b/commands/msg/mark.go
@@ -4,6 +4,7 @@ import (
"fmt"
"git.sr.ht/~rjarry/aerc/commands"
+ "git.sr.ht/~rjarry/aerc/models"
)
type Mark struct {
@@ -28,7 +29,7 @@ func (Mark) Aliases() []string {
func (m Mark) Execute(args []string) error {
h := newHelper()
- OnSelectedMessage := func(fn func(uint32)) error {
+ OnSelectedMessage := func(fn func(models.UID)) error {
if fn == nil {
return fmt.Errorf("no operation selected")
}
@@ -58,7 +59,7 @@ func (m Mark) Execute(args []string) error {
switch args[0] {
case "mark":
- var modFunc func(uint32)
+ var modFunc func(models.UID)
if m.Toggle {
modFunc = marker.ToggleMark
} else {
diff --git a/commands/msg/move.go b/commands/msg/move.go
index 96e7c3da..1365b060 100644
--- a/commands/msg/move.go
+++ b/commands/msg/move.go
@@ -119,7 +119,7 @@ func (m Move) Execute(args []string) error {
// something is happening
app.PushStatus("Moving messages...", 10*time.Second)
- var appended []uint32
+ var appended []models.UID
var timeout bool
go func() {
defer log.PanicHandler()
@@ -187,7 +187,7 @@ func (m Move) Execute(args []string) error {
func (m Move) CallBack(
msg types.WorkerMessage,
acct *app.AccountView,
- uids []uint32,
+ uids []models.UID,
next *models.MessageInfo,
marker marker.Marker,
timeout bool,
diff --git a/commands/msg/pipe.go b/commands/msg/pipe.go
index 6ba57089..9764de82 100644
--- a/commands/msg/pipe.go
+++ b/commands/msg/pipe.go
@@ -15,6 +15,7 @@ import (
"git.sr.ht/~rjarry/aerc/commands"
cryptoutil "git.sr.ht/~rjarry/aerc/lib/crypto/util"
"git.sr.ht/~rjarry/aerc/lib/log"
+ "git.sr.ht/~rjarry/aerc/models"
mboxer "git.sr.ht/~rjarry/aerc/worker/mbox"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -122,7 +123,7 @@ func (p Pipe) Run(cb func()) error {
app.PushStatus("Fetching messages ...", 10*time.Second)
if p.Full {
- var uids []uint32
+ var uids []models.UID
var title string
h := newHelper()
diff --git a/commands/msg/read.go b/commands/msg/read.go
index ab84d51c..686c1a9e 100644
--- a/commands/msg/read.go
+++ b/commands/msg/read.go
@@ -97,8 +97,8 @@ func (f FlagMsg) Execute(args []string) error {
}
// UIDs of messages to enable or disable the flag for.
- var toEnable []uint32
- var toDisable []uint32
+ var toEnable []models.UID
+ var toDisable []models.UID
if f.Toggle {
// If toggling, split messages into those that need to
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 7c59ac85..15566414 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -70,7 +70,7 @@ func (r Recall) Execute(args []string) error {
}
composer.Tab = app.NewTab(composer, subject)
composer.OnClose(func(composer *app.Composer) {
- uids := []uint32{msgInfo.Uid}
+ uids := []models.UID{msgInfo.Uid}
deleteMessage := func() {
store.Delete(
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index e55b5d4b..2654514e 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -195,13 +195,13 @@ func (r reply) Execute(args []string) error {
composer.OnClose(func(c *app.Composer) {
switch {
case c.Sent() && c.Archive() != "" && !noStore:
- store.Answered([]uint32{msg.Uid}, true, nil)
+ store.Answered([]models.UID{msg.Uid}, true, nil)
err := archive([]*models.MessageInfo{msg}, nil, c.Archive())
if err != nil {
app.PushStatus("Archive failed", 10*time.Second)
}
case c.Sent() && !noStore:
- store.Answered([]uint32{msg.Uid}, true, nil)
+ store.Answered([]models.UID{msg.Uid}, true, nil)
case mv != nil && r.Close:
view := account.ViewMessage{Peek: true}
//nolint:errcheck // who cares?
diff --git a/commands/msg/utils.go b/commands/msg/utils.go
index d6dffd50..f6acb10d 100644
--- a/commands/msg/utils.go
+++ b/commands/msg/utils.go
@@ -29,7 +29,7 @@ func newHelper() *helper {
}
}
-func (h *helper) markedOrSelectedUids() ([]uint32, error) {
+func (h *helper) markedOrSelectedUids() ([]models.UID, error) {
return commands.MarkedOrSelected(h.msgProvider)
}
diff --git a/commands/util.go b/commands/util.go
index b6a458fd..bb20a204 100644
--- a/commands/util.go
+++ b/commands/util.go
@@ -148,7 +148,7 @@ func listDir(path string, hidden bool) []string {
// MarkedOrSelected returns either all marked messages if any are marked or the
// selected message instead
-func MarkedOrSelected(pm app.ProvidesMessages) ([]uint32, error) {
+func MarkedOrSelected(pm app.ProvidesMessages) ([]models.UID, error) {
// marked has priority over the selected message
marked, err := pm.MarkedMessages()
if err != nil {
@@ -162,15 +162,15 @@ func MarkedOrSelected(pm app.ProvidesMessages) ([]uint32, error) {
if err != nil {
return nil, err
}
- return expandFoldedThreads(pm, []uint32{msg.Uid}), nil
+ return expandFoldedThreads(pm, []models.UID{msg.Uid}), nil
}
-func expandFoldedThreads(pm app.ProvidesMessages, uids []uint32) []uint32 {
+func expandFoldedThreads(pm app.ProvidesMessages, uids []models.UID) []models.UID {
store := pm.Store()
if store == nil {
return uids
}
- expanded := make([]uint32, len(uids))
+ expanded := make([]models.UID, len(uids))
copy(expanded, uids)
for _, uid := range uids {
thread, err := store.Thread(uid)
@@ -194,8 +194,8 @@ func expandFoldedThreads(pm app.ProvidesMessages, uids []uint32) []uint32 {
}
// UidsFromMessageInfos extracts a uid slice from a slice of MessageInfos
-func UidsFromMessageInfos(msgs []*models.MessageInfo) []uint32 {
- uids := make([]uint32, len(msgs))
+func UidsFromMessageInfos(msgs []*models.MessageInfo) []models.UID {
+ uids := make([]models.UID, len(msgs))
i := 0
for _, msg := range msgs {
uids[i] = msg.Uid
@@ -204,9 +204,9 @@ func UidsFromMessageInfos(msgs []*models.MessageInfo) []uint32 {
return uids
}
-func MsgInfoFromUids(store *lib.MessageStore, uids []uint32, statusInfo func(string)) ([]*models.MessageInfo, error) {
+func MsgInfoFromUids(store *lib.MessageStore, uids []models.UID, statusInfo func(string)) ([]*models.MessageInfo, error) {
infos := make([]*models.MessageInfo, len(uids))
- needHeaders := make([]uint32, 0)
+ needHeaders := make([]models.UID, 0)
for i, uid := range uids {
var ok bool
infos[i], ok = store.Messages[uid]
diff --git a/lib/emlview.go b/lib/emlview.go
index e27edcb0..3f9c3f39 100644
--- a/lib/emlview.go
+++ b/lib/emlview.go
@@ -19,8 +19,8 @@ func (fm *EmlMessage) NewReader() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(*fm)), nil
}
-func (fm *EmlMessage) UID() uint32 {
- return 0xFFFFFFF
+func (fm *EmlMessage) UID() models.UID {
+ return ""
}
func (fm *EmlMessage) Labels() ([]string, error) {
diff --git a/lib/iterator/impl.go b/lib/iterator/impl.go
index 5e685166..be8c382e 100644
--- a/lib/iterator/impl.go
+++ b/lib/iterator/impl.go
@@ -3,6 +3,7 @@ package iterator
import (
"errors"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -11,7 +12,7 @@ type defaultFactory struct{}
func (df *defaultFactory) NewIterator(a interface{}) Iterator {
switch data := a.(type) {
- case []uint32:
+ case []models.UID:
return &defaultUid{data: data, index: len(data)}
case []*types.Thread:
return &defaultThread{data: data, index: len(data)}
@@ -21,7 +22,7 @@ func (df *defaultFactory) NewIterator(a interface{}) Iterator {
// defaultUid
type defaultUid struct {
- data []uint32
+ data []models.UID
index int
}
@@ -70,7 +71,7 @@ type reverseFactory struct{}
func (rf *reverseFactory) NewIterator(a interface{}) Iterator {
switch data := a.(type) {
- case []uint32:
+ case []models.UID:
return &reverseUid{data: data, index: -1}
case []*types.Thread:
return &reverseThread{data: data, index: -1}
@@ -80,7 +81,7 @@ func (rf *reverseFactory) NewIterator(a interface{}) Iterator {
// reverseUid
type reverseUid struct {
- data []uint32
+ data []models.UID
index int
}
diff --git a/lib/iterator/iterator_test.go b/lib/iterator/iterator_test.go
index 6a8d3f6e..01ab9ff9 100644
--- a/lib/iterator/iterator_test.go
+++ b/lib/iterator/iterator_test.go
@@ -4,10 +4,11 @@ import (
"testing"
"git.sr.ht/~rjarry/aerc/lib/iterator"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
-func toThreads(uids []uint32) []*types.Thread {
+func toThreads(uids []models.UID) []*types.Thread {
threads := make([]*types.Thread, len(uids))
for i, u := range uids {
threads[i] = &types.Thread{Uid: u}
@@ -16,8 +17,8 @@ func toThreads(uids []uint32) []*types.Thread {
}
func TestIterator_DefaultFactory(t *testing.T) {
- input := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9}
- want := []uint32{9, 8, 7, 6, 5, 4, 3, 2, 1}
+ input := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
+ want := []models.UID{"9", "8", "7", "6", "5", "4", "3", "2", "1"}
factory := iterator.NewFactory(false)
if factory == nil {
@@ -30,8 +31,8 @@ func TestIterator_DefaultFactory(t *testing.T) {
}
func TestIterator_ReverseFactory(t *testing.T) {
- input := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9}
- want := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9}
+ input := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
+ want := []models.UID{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
factory := iterator.NewFactory(true)
if factory == nil {
@@ -45,13 +46,13 @@ func TestIterator_ReverseFactory(t *testing.T) {
}
func checkUids(t *testing.T, factory iterator.Factory,
- input []uint32, want []uint32, start, end int,
+ input []models.UID, want []models.UID, start, end int,
) {
label := "uids"
- got := make([]uint32, 0)
+ got := make([]models.UID, 0)
iter := factory.NewIterator(input)
for iter.Next() {
- got = append(got, iter.Value().(uint32))
+ got = append(got, iter.Value().(models.UID))
}
if len(got) != len(want) {
t.Errorf(label + "number of elements not correct")
diff --git a/lib/marker/marker.go b/lib/marker/marker.go
index a0860be9..55f70f42 100644
--- a/lib/marker/marker.go
+++ b/lib/marker/marker.go
@@ -1,13 +1,15 @@
package marker
+import "git.sr.ht/~rjarry/aerc/models"
+
// Marker provides the interface for the marking behavior of messages
type Marker interface {
- Mark(uint32)
- Unmark(uint32)
- ToggleMark(uint32)
+ Mark(models.UID)
+ Unmark(models.UID)
+ ToggleMark(models.UID)
Remark()
- Marked() []uint32
- IsMarked(uint32) bool
+ Marked() []models.UID
+ IsMarked(models.UID) bool
IsVisualMark() bool
ToggleVisualMark(bool)
UpdateVisualMark()
@@ -16,30 +18,30 @@ type Marker interface {
// UIDProvider provides the underlying uids and the selected message index
type UIDProvider interface {
- Uids() []uint32
+ Uids() []models.UID
SelectedIndex() int
}
type controller struct {
uidProvider UIDProvider
- marked map[uint32]struct{}
- lastMarked map[uint32]struct{}
- visualStartUID uint32
+ marked map[models.UID]struct{}
+ lastMarked map[models.UID]struct{}
+ visualStartUID models.UID
visualMarkMode bool
- visualBase map[uint32]struct{}
+ visualBase map[models.UID]struct{}
}
// New returns a new Marker
func New(up UIDProvider) Marker {
return &controller{
uidProvider: up,
- marked: make(map[uint32]struct{}),
- lastMarked: make(map[uint32]struct{}),
+ marked: make(map[models.UID]struct{}),
+ lastMarked: make(map[models.UID]struct{}),
}
}
// Mark markes the uid as marked
-func (mc *controller) Mark(uid uint32) {
+func (mc *controller) Mark(uid models.UID) {
if mc.visualMarkMode {
// visual mode has override, bogus input from user
return
@@ -48,7 +50,7 @@ func (mc *controller) Mark(uid uint32) {
}
// Unmark unmarks the uid
-func (mc *controller) Unmark(uid uint32) {
+func (mc *controller) Unmark(uid models.UID) {
if mc.visualMarkMode {
// user probably wanted to clear the visual marking
mc.ClearVisualMark()
@@ -63,7 +65,7 @@ func (mc *controller) Remark() {
}
// ToggleMark toggles the marked state for the given uid
-func (mc *controller) ToggleMark(uid uint32) {
+func (mc *controller) ToggleMark(uid models.UID) {
if mc.visualMarkMode {
// visual mode has override, bogus input from user
return
@@ -78,7 +80,7 @@ func (mc *controller) ToggleMark(uid uint32) {
// resetMark removes the marking from all messages
func (mc *controller) resetMark() {
mc.lastMarked = mc.marked
- mc.marked = make(map[uint32]struct{})
+ mc.marked = make(map[models.UID]struct{})
}
// removeStaleUID removes uids that are no longer presents in the UIDProvider
@@ -98,15 +100,15 @@ func (mc *controller) removeStaleUID() {
}
// IsMarked checks whether the given uid has been marked
-func (mc *controller) IsMarked(uid uint32) bool {
+func (mc *controller) IsMarked(uid models.UID) bool {
_, marked := mc.marked[uid]
return marked
}
// Marked returns the uids of all marked messages
-func (mc *controller) Marked() []uint32 {
+func (mc *controller) Marked() []models.UID {
mc.removeStaleUID()
- marked := make([]uint32, len(mc.marked))
+ marked := make([]models.UID, len(mc.marked))
i := 0
for uid := range mc.marked {
marked[i] = uid
@@ -132,7 +134,7 @@ func (mc *controller) ToggleVisualMark(clear bool) {
if idx := mc.uidProvider.SelectedIndex(); idx >= 0 && idx < len(uids) {
mc.visualStartUID = uids[idx]
mc.marked[mc.visualStartUID] = struct{}{}
- mc.visualBase = make(map[uint32]struct{})
+ mc.visualBase = make(map[models.UID]struct{})
for key, value := range mc.marked {
mc.visualBase[key] = value
}
@@ -144,7 +146,7 @@ func (mc *controller) ToggleVisualMark(clear bool) {
func (mc *controller) ClearVisualMark() {
mc.resetMark()
mc.visualMarkMode = false
- mc.visualStartUID = 0
+ mc.visualStartUID = ""
}
// UpdateVisualMark updates the index with the currently selected message
@@ -167,13 +169,13 @@ func (mc *controller) UpdateVisualMark() {
uids := mc.uidProvider.Uids()
- var visUids []uint32
+ var visUids []models.UID
if selectedIdx > startIdx {
visUids = uids[startIdx : selectedIdx+1]
} else {
visUids = uids[selectedIdx : startIdx+1]
}
- mc.marked = make(map[uint32]struct{})
+ mc.marked = make(map[models.UID]struct{})
for uid := range mc.visualBase {
mc.marked[uid] = struct{}{}
}
diff --git a/lib/marker/marker_test.go b/lib/marker/marker_test.go
index df9eb2a3..04d3fedc 100644
--- a/lib/marker/marker_test.go
+++ b/lib/marker/marker_test.go
@@ -4,16 +4,17 @@ import (
"testing"
"git.sr.ht/~rjarry/aerc/lib/marker"
+ "git.sr.ht/~rjarry/aerc/models"
)
// mockUidProvider implements the UidProvider interface and mocks the message
// store for testing
type mockUidProvider struct {
- uids []uint32
+ uids []models.UID
idx int
}
-func (mock *mockUidProvider) Uids() []uint32 {
+func (mock *mockUidProvider) Uids() []models.UID {
return mock.uids
}
@@ -23,7 +24,7 @@ func (mock *mockUidProvider) SelectedIndex() int {
func createMarker() (marker.Marker, *mockUidProvider) {
uidProvider := &mockUidProvider{
- uids: []uint32{1, 2, 3, 4},
+ uids: []models.UID{"1", "2", "3", "4"},
idx: 1,
}
m := marker.New(uidProvider)
@@ -32,7 +33,7 @@ func createMarker() (marker.Marker, *mockUidProvider) {
func TestMarker_MarkUnmark(t *testing.T) {
m, _ := createMarker()
- uid := uint32(4)
+ uid := models.UID("4")
m.Mark(uid)
if !m.IsMarked(uid) {
@@ -47,7 +48,7 @@ func TestMarker_MarkUnmark(t *testing.T) {
func TestMarker_ToggleMark(t *testing.T) {
m, _ := createMarker()
- uid := uint32(4)
+ uid := models.UID("4")
if m.IsMarked(uid) {
t.Errorf("ToggleMark: uid should not be marked")
@@ -66,9 +67,9 @@ func TestMarker_ToggleMark(t *testing.T) {
func TestMarker_Marked(t *testing.T) {
m, _ := createMarker()
- expected := map[uint32]struct{}{
- uint32(1): {},
- uint32(4): {},
+ expected := map[models.UID]struct{}{
+ "1": {},
+ "4": {},
}
for uid := range expected {
m.Mark(uid)
@@ -81,7 +82,7 @@ func TestMarker_Marked(t *testing.T) {
for _, uid := range got {
if _, ok := expected[uid]; !ok {
- t.Errorf("Marked: received uid %d as marked but it should not be", uid)
+ t.Errorf("Marked: received uid %q as marked but it should not be", uid)
}
}
}
@@ -93,15 +94,15 @@ func TestMarker_VisualMode(t *testing.T) {
m.ToggleVisualMark(false)
// marking should now fail silently because we're in visual mode
- m.Mark(1)
- if m.IsMarked(1) {
+ m.Mark("1")
+ if m.IsMarked("1") {
t.Errorf("marking in visual mode should not work")
}
// move selection index to last item
up.idx = len(up.uids) - 1
m.UpdateVisualMark()
- expectedMarked := []uint32{2, 3, 4}
+ expectedMarked := []models.UID{"2", "3", "4"}
for _, uidMarked := range expectedMarked {
if !m.IsMarked(uidMarked) {
@@ -128,7 +129,7 @@ func TestMarker_VisualMode(t *testing.T) {
func TestMarker_MarkOutOfBound(t *testing.T) {
m, _ := createMarker()
- outOfBoundUid := uint32(100)
+ outOfBoundUid := models.UID("100")
m.Mark(outOfBoundUid)
for _, markedUid := range m.Marked() {
diff --git a/lib/messageview.go b/lib/messageview.go
index 34549023..b849148a 100644
--- a/lib/messageview.go
+++ b/lib/messageview.go
@@ -75,7 +75,7 @@ func NewMessageStoreView(messageInfo *models.MessageInfo, setSeen bool,
cb := func(msv MessageView, err error) {
if msv != nil && setSeen && err == nil &&
!messageInfo.Flags.Has(models.SeenFlag) {
- store.Flag([]uint32{messageInfo.Uid}, models.SeenFlag, true, nil)
+ store.Flag([]models.UID{messageInfo.Uid}, models.SeenFlag, true, nil)
}
innerCb(msv, err)
}
@@ -147,7 +147,7 @@ func (msv *MessageStoreView) MessageDetails() *models.MessageDetails {
func (msv *MessageStoreView) FetchFull(cb func(io.Reader)) {
if msv.message == nil && msv.messageStore != nil {
- msv.messageStore.FetchFull([]uint32{msv.messageInfo.Uid},
+ msv.messageStore.FetchFull([]models.UID{msv.messageInfo.Uid},
func(fm *types.FullMessage) {
cb(fm.Content.Reader)
})
diff --git a/lib/msgstore.go b/lib/msgstore.go
index 5c8b3ef1..1250a9c0 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -20,8 +20,8 @@ import (
type MessageStore struct {
sync.Mutex
Name string
- Deleted map[uint32]interface{}
- Messages map[uint32]*models.MessageInfo
+ Deleted map[models.UID]interface{}
+ Messages map[models.UID]*models.MessageInfo
Sorting bool
ui func() *config.UIConfig
@@ -30,21 +30,21 @@ type MessageStore struct {
ctx context.Context
// Ordered list of known UIDs
- uids []uint32
+ uids []models.UID
threads []*types.Thread
// Visible UIDs
scrollOffset int
scrollLen int
- selectedUid uint32
- bodyCallbacks map[uint32][]func(*types.FullMessage)
+ selectedUid models.UID
+ bodyCallbacks map[models.UID][]func(*types.FullMessage)
// marking
marker marker.Marker
// Search/filter results
- results []uint32
+ results []models.UID
resultIndex int
filter *types.SearchCriteria
@@ -60,11 +60,11 @@ type MessageStore struct {
onUpdate func(store *MessageStore) // TODO: multiple onUpdate handlers
onFilterChange func(store *MessageStore)
onUpdateDirs func()
- pendingBodies map[uint32]interface{}
- pendingHeaders map[uint32]interface{}
+ pendingBodies map[models.UID]interface{}
+ pendingHeaders map[models.UID]interface{}
worker *types.Worker
- needsFlags []uint32
+ needsFlags []models.UID
fetchFlagsDebounce *time.Timer
fetchFlagsDelay time.Duration
@@ -85,7 +85,7 @@ type MessageStore struct {
onSelect func(*models.MessageInfo)
}
-const MagicUid = 0xFFFFFFFF
+const MagicUid = models.UID("")
func NewMessageStore(worker *types.Worker, name string,
ui func() *config.UIConfig,
@@ -97,8 +97,8 @@ func NewMessageStore(worker *types.Worker, name string,
) *MessageStore {
return &MessageStore{
Name: name,
- Deleted: make(map[uint32]interface{}),
- Messages: make(map[uint32]*models.MessageInfo),
+ Deleted: make(map[models.UID]interface{}),
+ Messages: make(map[models.UID]*models.MessageInfo),
ui: ui,
@@ -108,13 +108,12 @@ func NewMessageStore(worker *types.Worker, name string,
// default window height until account is drawn once
scrollLen: 25,
- bodyCallbacks: make(map[uint32][]func(*types.FullMessage)),
-
- pendingBodies: make(map[uint32]interface{}),
- pendingHeaders: make(map[uint32]interface{}),
+ bodyCallbacks: make(map[models.UID][]func(*types.FullMessage)),
+ pendingBodies: make(map[models.UID]interface{}),
+ pendingHeaders: make(map[models.UID]interface{}),
worker: worker,
- needsFlags: []uint32{},
+ needsFlags: []models.UID{},
fetchFlagsDelay: 50 * time.Millisecond,
triggerNewEmail: triggerNewEmail,
@@ -158,12 +157,12 @@ func (store *MessageStore) UpdateScroll(offset, length int) {
store.scrollLen = length
}
-func (store *MessageStore) FetchHeaders(uids []uint32,
+func (store *MessageStore) FetchHeaders(uids []models.UID,
cb func(types.WorkerMessage),
) {
// TODO: this could be optimized by pre-allocating toFetch and trimming it
// at the end. In practice we expect to get most messages back in one frame.
- var toFetch []uint32
+ var toFetch []models.UID
for _, uid := range uids {
if _, ok := store.pendingHeaders[uid]; !ok {
toFetch = append(toFetch, uid)
@@ -189,10 +188,10 @@ func (store *MessageStore) FetchHeaders(uids []uint32,
}
}
-func (store *MessageStore) FetchFull(uids []uint32, cb func(*types.FullMessage)) {
+func (store *MessageStore) FetchFull(uids []models.UID, cb func(*types.FullMessage)) {
// TODO: this could be optimized by pre-allocating toFetch and trimming it
// at the end. In practice we expect to get most messages back in one frame.
- var toFetch []uint32
+ var toFetch []models.UID
for _, uid := range uids {
if _, ok := store.pendingBodies[uid]; !ok {
toFetch = append(toFetch, uid)
@@ -220,7 +219,7 @@ func (store *MessageStore) FetchFull(uids []uint32, cb func(*types.FullMessage))
}
}
-func (store *MessageStore) FetchBodyPart(uid uint32, part []int, cb func(io.Reader)) {
+func (store *MessageStore) FetchBodyPart(uid models.UID, part []int, cb func(io.Reader)) {
store.worker.PostAction(&types.FetchMessageBodyPart{
Uid: uid,
Part: part,
@@ -253,7 +252,7 @@ func merge(to *models.MessageInfo, from *models.MessageInfo) {
}
func (store *MessageStore) Update(msg types.WorkerMessage) {
- var newUids []uint32
+ var newUids []models.UID
update := false
updateThreads := false
directoryChange := false
@@ -265,7 +264,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
store.Sort(store.sortCriteria, nil)
update = true
case *types.DirectoryContents:
- newMap := make(map[uint32]*models.MessageInfo, len(msg.Uids))
+ newMap := make(map[models.UID]*models.MessageInfo, len(msg.Uids))
for i, uid := range msg.Uids {
if msg, ok := store.Messages[uid]; ok {
newMap[uid] = msg
@@ -291,7 +290,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
store.uids = store.builder.Uids()
store.threads = msg.Threads
- newMap := make(map[uint32]*models.MessageInfo, len(store.uids))
+ newMap := make(map[models.UID]*models.MessageInfo, len(store.uids))
for i, uid := range store.uids {
if msg, ok := store.Messages[uid]; ok {
newMap[uid] = msg
@@ -351,13 +350,13 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
break
}
- toDelete := make(map[uint32]interface{})
+ toDelete := make(map[models.UID]interface{})
for _, uid := range msg.Uids {
toDelete[uid] = nil
delete(store.Messages, uid)
delete(store.Deleted, uid)
}
- uids := make([]uint32, 0, len(store.uids)-len(msg.Uids))
+ uids := make([]models.UID, 0, len(store.uids)-len(msg.Uids))
for _, uid := range store.uids {
if _, deleted := toDelete[uid]; deleted {
continue
@@ -369,7 +368,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) {
store.Select(MagicUid)
}
- var newResults []uint32
+ var newResults []models.UID
for _, res := range store.results {
if _, deleted := toDelete[res]; !deleted {
newResults = append(newResults, res)
@@ -528,7 +527,7 @@ func (store *MessageStore) runThreadBuilderNow() {
}
// Thread returns the thread for the given UId
-func (store *MessageStore) Thread(uid uint32) (*types.Thread, error) {
+func (store *MessageStore) Thread(uid models.UID) (*types.Thread, error) {
if store.builder == nil {
return nil, errors.New("no threads found")
}
@@ -540,15 +539,15 @@ func (store *MessageStore) SelectedThread() (*types.Thread, error) {
return store.Thread(store.SelectedUid())
}
-func (store *MessageStore) Fold(uid uint32, toggle bool) error {
+func (store *MessageStore) Fold(uid models.UID, toggle bool) error {
return store.doThreadFolding(uid, true, toggle)
}
-func (store *MessageStore) Unfold(uid uint32, toggle bool) error {
+func (store *MessageStore) Unfold(uid models.UID, toggle bool) error {
return store.doThreadFolding(uid, false, toggle)
}
-func (store *MessageStore) doThreadFolding(uid uint32, hide bool, toggle bool) error {
+func (store *MessageStore) doThreadFolding(uid models.UID, hide bool, toggle bool) error {
thread, err := store.Thread(uid)
if err != nil {
return err
@@ -596,7 +595,7 @@ func (store *MessageStore) doThreadFolding(uid uint32, hide bool, toggle bool) e
return nil
}
-func (store *MessageStore) Delete(uids []uint32, mfs *types.MultiFileStrategy,
+func (store *MessageStore) Delete(uids []models.UID, mfs *types.MultiFileStrategy,
cb func(msg types.WorkerMessage),
) {
for _, uid := range uids {
@@ -618,13 +617,13 @@ func (store *MessageStore) Delete(uids []uint32, mfs *types.MultiFileStrategy,
})
}
-func (store *MessageStore) revertDeleted(uids []uint32) {
+func (store *MessageStore) revertDeleted(uids []models.UID) {
for _, uid := range uids {
delete(store.Deleted, uid)
}
}
-func (store *MessageStore) Copy(uids []uint32, dest string, createDest bool,
+func (store *MessageStore) Copy(uids []models.UID, dest string, createDest bool,
mfs *types.MultiFileStrategy, cb func(msg types.WorkerMessage),
) {
if createDest {
@@ -646,7 +645,7 @@ func (store *MessageStore) Copy(uids []uint32, dest string, createDest bool,
})
}
-func (store *MessageStore) Move(uids []uint32, dest string, createDest bool,
+func (store *MessageStore) Move(uids []models.UID, dest string, createDest bool,
mfs *types.MultiFileStrategy, cb func(msg types.WorkerMessage),
) {
for _, uid := range uids {
@@ -699,7 +698,7 @@ func (store *MessageStore) Append(dest string, flags models.Flags, date time.Tim
})
}
-func (store *MessageStore) Flag(uids []uint32, flags models.Flags,
+func (store *MessageStore) Flag(uids []models.UID, flags models.Flags,
enable bool, cb func(msg types.WorkerMessage),
) {
store.worker.PostAction(&types.FlagMessages{
@@ -729,7 +728,7 @@ func (store *MessageStore) Flag(uids []uint32, flags models.Flags,
})
}
-func (store *MessageStore) Answered(uids []uint32, answered bool,
+func (store *MessageStore) Answered(uids []models.UID, answered bool,
cb func(msg types.WorkerMessage),
) {
store.worker.PostAction(&types.AnsweredMessages{
@@ -738,7 +737,7 @@ func (store *MessageStore) Answered(uids []uint32, answered bool,
}, cb)
}
-func (store *MessageStore) Forwarded(uids []uint32, forwarded bool,
+func (store *MessageStore) Forwarded(uids []models.UID, forwarded bool,
cb func(msg types.WorkerMessage),
) {
store.worker.PostAction(&types.ForwardedMessages{
@@ -747,7 +746,7 @@ func (store *MessageStore) Forwarded(uids []uint32, forwarded bool,
}, cb)
}
-func (store *MessageStore) Uids() []uint32 {
+func (store *MessageStore) Uids() []models.UID {
if store.ThreadedView() && store.builder != nil {
if uids := store.builder.Uids(); len(uids) > 0 {
return uids
@@ -764,7 +763,7 @@ func (store *MessageStore) Selected() *models.MessageInfo {
return store.Messages[store.selectedUid]
}
-func (store *MessageStore) SelectedUid() uint32 {
+func (store *MessageStore) SelectedUid() models.UID {
if store.selectedUid == MagicUid && len(store.Uids()) > 0 {
iter := store.UidsIterator()
idx := iter.StartIndex()
@@ -776,14 +775,14 @@ func (store *MessageStore) SelectedUid() uint32 {
return store.selectedUid
}
-func (store *MessageStore) Select(uid uint32) {
+func (store *MessageStore) Select(uid models.UID) {
store.selectPriv(uid, false)
if store.onSelect != nil {
store.onSelect(store.Selected())
}
}
-func (store *MessageStore) selectPriv(uid uint32, lockHeld bool) {
+func (store *MessageStore) selectPriv(uid models.UID, lockHeld bool) {
if !lockHeld {
store.threadsMutex.Lock()
}
@@ -844,14 +843,14 @@ func (store *MessageStore) Prev() {
store.NextPrev(-1)
}
-func (store *MessageStore) Search(terms *types.SearchCriteria, cb func([]uint32)) {
+func (store *MessageStore) Search(terms *types.SearchCriteria, cb func([]models.UID)) {
store.worker.PostAction(&types.SearchDirectory{
Context: store.ctx,
Criteria: terms,
}, func(msg types.WorkerMessage) {
if msg, ok := msg.(*types.SearchResults); ok {
allowedUids := store.Uids()
- uids := make([]uint32, 0, len(msg.Uids))
+ uids := make([]models.UID, 0, len(msg.Uids))
for _, uid := range msg.Uids {
for _, uidCheck := range allowedUids {
if uid == uidCheck {
@@ -866,14 +865,14 @@ func (store *MessageStore) Search(terms *types.SearchCriteria, cb func([]uint32)
})
}
-func (store *MessageStore) ApplySearch(results []uint32) {
+func (store *MessageStore) ApplySearch(results []models.UID) {
store.results = results
store.resultIndex = -1
store.NextResult()
}
// IsResult returns true if uid is a search result
-func (store *MessageStore) IsResult(uid uint32) bool {
+func (store *MessageStore) IsResult(uid models.UID) bool {
for _, hit := range store.results {
if hit == uid {
return true
@@ -935,7 +934,7 @@ func (store *MessageStore) PrevResult() {
store.nextPrevResult(-1)
}
-func (store *MessageStore) ModifyLabels(uids []uint32, add, remove []string,
+func (store *MessageStore) ModifyLabels(uids []models.UID, add, remove []string,
cb func(msg types.WorkerMessage),
) {
store.worker.PostAction(&types.ModifyLabels{
@@ -999,7 +998,7 @@ func (store *MessageStore) Marker() marker.Marker {
}
// FindIndexByUid returns the index in store.Uids() or -1 if not found
-func (store *MessageStore) FindIndexByUid(uid uint32) int {
+func (store *MessageStore) FindIndexByUid(uid models.UID) int {
for idx, u := range store.Uids() {
if u == uid {
return idx
@@ -1029,7 +1028,7 @@ func (store *MessageStore) fetchFlags() {
Context: store.ctx,
Uids: store.needsFlags,
}, nil)
- store.needsFlags = []uint32{}
+ store.needsFlags = []models.UID{}
store.Unlock()
})
}
diff --git a/lib/rfc822/message.go b/lib/rfc822/message.go
index 3f0c447e..653cb2fe 100644
--- a/lib/rfc822/message.go
+++ b/lib/rfc822/message.go
@@ -291,7 +291,7 @@ type RawMessage interface {
NewReader() (io.ReadCloser, error)
ModelFlags() (models.Flags, error)
Labels() ([]string, error)
- UID() uint32
+ UID() models.UID
}
// MessageInfo populates a models.MessageInfo struct for the message.
diff --git a/lib/rfc822/message_test.go b/lib/rfc822/message_test.go
index f5f222e4..ca5ae014 100644
--- a/lib/rfc822/message_test.go
+++ b/lib/rfc822/message_test.go
@@ -185,7 +185,7 @@ func (m *mockRawMessage) NewReader() (io.ReadCloser, error) {
}
func (m *mockRawMessage) ModelFlags() (models.Flags, error) { return 0, nil }
func (m *mockRawMessage) Labels() ([]string, error) { return nil, nil }
-func (m *mockRawMessage) UID() uint32 { return 0 }
+func (m *mockRawMessage) UID() models.UID { return "" }
func die(err error) {
if err != nil {
diff --git a/lib/sort/sort.go b/lib/sort/sort.go
index 082ba48b..b62b8d04 100644
--- a/lib/sort/sort.go
+++ b/lib/sort/sort.go
@@ -6,6 +6,7 @@ import (
"sort"
"strings"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -61,9 +62,9 @@ func parseSortField(arg string) (types.SortField, error) {
// Sorts toSort by sortBy so that toSort becomes a permutation following the
// order of sortBy.
// toSort should be a subset of sortBy
-func SortBy(toSort []uint32, sortBy []uint32) {
+func SortBy(toSort []models.UID, sortBy []models.UID) {
// build a map from sortBy
- uidMap := make(map[uint32]int)
+ uidMap := make(map[models.UID]int)
for i, uid := range sortBy {
uidMap[uid] = i
}
diff --git a/lib/threadbuilder.go b/lib/threadbuilder.go
index abfbadb7..d7373b10 100644
--- a/lib/threadbuilder.go
+++ b/lib/threadbuilder.go
@@ -15,41 +15,41 @@ import (
type ThreadBuilder struct {
sync.Mutex
- threadBlocks map[uint32]jwz.Threadable
- threadedUids []uint32
- threadMap map[uint32]*types.Thread
+ threadBlocks map[models.UID]jwz.Threadable
+ threadedUids []models.UID
+ threadMap map[models.UID]*types.Thread
iterFactory iterator.Factory
bySubject bool
}
func NewThreadBuilder(i iterator.Factory, bySubject bool) *ThreadBuilder {
tb := &ThreadBuilder{
- threadBlocks: make(map[uint32]jwz.Threadable),
+ threadBlocks: make(map[models.UID]jwz.Threadable),
iterFactory: i,
- threadMap: make(map[uint32]*types.Thread),
+ threadMap: make(map[models.UID]*types.Thread),
bySubject: bySubject,
}
return tb
}
-func (builder *ThreadBuilder) ThreadForUid(uid uint32) (*types.Thread, error) {
+func (builder *ThreadBuilder) ThreadForUid(uid models.UID) (*types.Thread, error) {
builder.Lock()
defer builder.Unlock()
t, ok := builder.threadMap[uid]
var err error
if !ok {
- err = fmt.Errorf("no thread found for uid '%d'", uid)
+ err = fmt.Errorf("no thread found for uid '%s'", uid)
}
return t, err
}
// Uids returns the uids in threading order
-func (builder *ThreadBuilder) Uids() []uint32 {
+func (builder *ThreadBuilder) Uids() []models.UID {
builder.Lock()
defer builder.Unlock()
if builder.threadedUids == nil {
- return []uint32{}
+ return []models.UID{}
}
return builder.threadedUids
}
@@ -68,7 +68,7 @@ func (builder *ThreadBuilder) Update(msg *models.MessageInfo) {
}
// Threads returns a slice of threads for the given list of uids
-func (builder *ThreadBuilder) Threads(uids []uint32, inverse bool, sort bool,
+func (builder *ThreadBuilder) Threads(uids []models.UID, inverse bool, sort bool,
) []*types.Thread {
builder.Lock()
defer builder.Unlock()
@@ -91,7 +91,7 @@ func (builder *ThreadBuilder) Threads(uids []uint32, inverse bool, sort bool,
return threads
}
-func (builder *ThreadBuilder) generateStructure(uids []uint32) jwz.Threadable {
+func (builder *ThreadBuilder) generateStructure(uids []models.UID) jwz.Threadable {
jwzThreads := make([]jwz.Threadable, 0, len(builder.threadBlocks))
for _, uid := range uids {
if thr, ok := builder.threadBlocks[uid]; ok {
@@ -108,7 +108,7 @@ func (builder *ThreadBuilder) generateStructure(uids []uint32) jwz.Threadable {
}
func (builder *ThreadBuilder) buildAercThreads(structure jwz.Threadable,
- uids []uint32, sort bool,
+ uids []models.UID, sort bool,
) []*types.Thread {
threads := make([]*types.Thread, 0, len(builder.threadBlocks))
@@ -121,7 +121,7 @@ func (builder *ThreadBuilder) buildAercThreads(structure jwz.Threadable,
// prepare bigger function
var bigger func(l, r *types.Thread) bool
if sort {
- sortMap := make(map[uint32]int)
+ sortMap := make(map[models.UID]int)
for i, uid := range uids {
sortMap[uid] = i
}
@@ -148,7 +148,7 @@ func (builder *ThreadBuilder) buildAercThreads(structure jwz.Threadable,
}
// build thread tree
- root := &types.Thread{Uid: 0}
+ root := &types.Thread{}
builder.buildTree(structure, root, bigger, true)
// copy top-level threads to thread slice
@@ -197,16 +197,16 @@ func (builder *ThreadBuilder) newThread(c jwz.Threadable, parent *types.Thread,
return nil
}
-func (builder *ThreadBuilder) sortThreads(threads []*types.Thread, orderedUids []uint32) {
+func (builder *ThreadBuilder) sortThreads(threads []*types.Thread, orderedUids []models.UID) {
types.SortThreadsBy(threads, orderedUids)
}
// RebuildUids rebuilds the uids from the given slice of threads
func (builder *ThreadBuilder) RebuildUids(threads []*types.Thread, inverse bool) {
- uids := make([]uint32, 0, len(threads))
+ uids := make([]models.UID, 0, len(threads))
iterT := builder.iterFactory.NewIterator(threads)
for iterT.Next() {
- var threaduids []uint32
+ var threaduids []models.UID
_ = iterT.Value().(*types.Thread).Walk(
func(t *types.Thread, level int, currentErr error) error {
stored, ok := builder.threadMap[t.Uid]
@@ -231,10 +231,10 @@ func (builder *ThreadBuilder) RebuildUids(threads []*types.Thread, inverse bool)
}
}
- result := make([]uint32, 0, len(uids))
+ result := make([]models.UID, 0, len(uids))
iterU := builder.iterFactory.NewIterator(uids)
for iterU.Next() {
- result = append(result, iterU.Value().(uint32))
+ result = append(result, iterU.Value().(models.UID))
}
builder.threadedUids = result
}
@@ -310,9 +310,9 @@ func cleanRefs(m, irp string, refs []string) []string {
return cleanRefs
}
-func (t *threadable) UID() uint32 {
+func (t *threadable) UID() models.UID {
if t.MsgInfo == nil {
- return 0
+ return ""
}
return t.MsgInfo.Uid
}
diff --git a/lib/uidstore/uidstore.go b/lib/uidstore/uidstore.go
deleted file mode 100644
index 11c5e47c..00000000
--- a/lib/uidstore/uidstore.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// Package uidstore provides a concurrency-safe two-way mapping between UIDs
-// used by the UI and arbitrary string keys as used by different mail backends.
-//
-// Multiple Store instances can safely be created and the UIDs that they
-// generate will be globally unique.
-package uidstore
-
-import (
- "sync"
- "sync/atomic"
-)
-
-var nextUID uint32 = 1
-
-// Store holds a mapping between application keys and globally-unique UIDs.
-type Store struct {
- keyByUID map[uint32]string
- uidByKey map[string]uint32
- m sync.Mutex
-}
-
-// NewStore creates a new, empty Store.
-func NewStore() *Store {
- return &Store{
- keyByUID: make(map[uint32]string),
- uidByKey: make(map[string]uint32),
- }
-}
-
-// GetOrInsert returns the UID for the provided key. If the key was already
-// present in the store, the same UID value is returned. Otherwise, the key is
-// inserted and the newly generated UID is returned.
-func (s *Store) GetOrInsert(key string) uint32 {
- s.m.Lock()
- defer s.m.Unlock()
- if uid, ok := s.uidByKey[key]; ok {
- return uid
- }
- uid := atomic.AddUint32(&nextUID, 1)
- s.keyByUID[uid] = key
- s.uidByKey[key] = uid
- return uid
-}
-
-// GetKey returns the key for the provided UID, if available.
-func (s *Store) GetKey(uid uint32) (string, bool) {
- s.m.Lock()
- defer s.m.Unlock()
- key, ok := s.keyByUID[uid]
- return key, ok
-}
-
-// RemoveUID removes the specified UID from the store.
-func (s *Store) RemoveUID(uid uint32) {
- s.m.Lock()
- defer s.m.Unlock()
- key, ok := s.keyByUID[uid]
- if ok {
- delete(s.uidByKey, key)
- }
- delete(s.keyByUID, uid)
-}
diff --git a/models/models.go b/models/models.go
index 749d14d9..0f7c8445 100644
--- a/models/models.go
+++ b/models/models.go
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io"
+ "strconv"
"strings"
"time"
@@ -107,6 +108,33 @@ func (c *Capabilities) Has(s string) bool {
return false
}
+type UID string
+
+func UidToUint32(uid UID) uint32 {
+ u, _ := strconv.ParseUint(string(uid), 10, 32)
+ return uint32(u)
+}
+
+func Uint32ToUid(u uint32) UID {
+ return UID(strconv.FormatUint(uint64(u), 10))
+}
+
+func UidToUint32List(uids []UID) []uint32 {
+ ulist := make([]uint32, 0, len(uids))
+ for _, uid := range uids {
+ ulist = append(ulist, UidToUint32(uid))
+ }
+ return ulist
+}
+
+func Uint32ToUidList(ulist []uint32) []UID {
+ uids := make([]UID, 0, len(ulist))
+ for _, u := range ulist {
+ uids = append(uids, Uint32ToUid(u))
+ }
+ return uids
+}
+
// A MessageInfo holds information about the structure of a message
type MessageInfo struct {
BodyStructure *BodyStructure
@@ -118,7 +146,7 @@ type MessageInfo struct {
RFC822Headers *mail.Header
Refs []string
Size uint32
- Uid uint32
+ Uid UID
Error error
}
@@ -169,13 +197,13 @@ func (mi *MessageInfo) References() ([]string, error) {
// A MessageBodyPart can be displayed in the message viewer
type MessageBodyPart struct {
Reader io.Reader
- Uid uint32
+ Uid UID
}
// A FullMessage is the entire message
type FullMessage struct {
Reader io.Reader
- Uid uint32
+ Uid UID
}
type BodyStructure struct {
diff --git a/worker/imap/cache.go b/worker/imap/cache.go
index 8ae60f03..0eba7b9d 100644
--- a/worker/imap/cache.go
+++ b/worker/imap/cache.go
@@ -24,7 +24,7 @@ type CachedHeader struct {
BodyStructure models.BodyStructure
Envelope models.Envelope
InternalDate time.Time
- Uid uint32
+ Uid models.UID
Size uint32
Header []byte
Created time.Time
@@ -34,7 +34,7 @@ var (
// cacheTag should be updated when changing the cache
// structure; this will ensure that the user's cache is cleared and
// reloaded when the underlying cache structure changes
- cacheTag = []byte("0002")
+ cacheTag = []byte("0003")
cacheTagKey = []byte("cache.tag")
)
@@ -112,9 +112,9 @@ func (w *IMAPWorker) cacheHeader(mi *models.MessageInfo) {
}
}
-func (w *IMAPWorker) getCachedHeaders(msg *types.FetchMessageHeaders) []uint32 {
+func (w *IMAPWorker) getCachedHeaders(msg *types.FetchMessageHeaders) []models.UID {
w.worker.Tracef("Retrieving headers from cache: %v", msg.Uids)
- var need []uint32
+ var need []models.UID
for _, uid := range msg.Uids {
key := w.headerKey(uid)
data, err := w.cache.Get(key, nil)
@@ -157,8 +157,8 @@ func (w *IMAPWorker) getCachedHeaders(msg *types.FetchMessageHeaders) []uint32 {
return need
}
-func (w *IMAPWorker) headerKey(uid uint32) []byte {
- key := fmt.Sprintf("header.%s.%d.%d",
+func (w *IMAPWorker) headerKey(uid models.UID) []byte {
+ key := fmt.Sprintf("header.%s.%d.%s",
w.selected.Name, w.selected.UidValidity, uid)
return []byte(key)
}
diff --git a/worker/imap/extensions/xgmext/client.go b/worker/imap/extensions/xgmext/client.go
index 65f11e74..e6e34a44 100644
--- a/worker/imap/extensions/xgmext/client.go
+++ b/worker/imap/extensions/xgmext/client.go
@@ -5,6 +5,7 @@ import (
"fmt"
"git.sr.ht/~rjarry/aerc/lib/log"
+ "git.sr.ht/~rjarry/aerc/models"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
"github.com/emersion/go-imap/commands"
@@ -19,7 +20,7 @@ func NewHandler(c *client.Client) *handler {
return &handler{client: c}
}
-func (h handler) FetchEntireThreads(requested []uint32) ([]uint32, error) {
+func (h handler) FetchEntireThreads(requested []models.UID) ([]models.UID, error) {
threadIds, err := h.fetchThreadIds(requested)
if err != nil {
return nil,
@@ -33,7 +34,7 @@ func (h handler) FetchEntireThreads(requested []uint32) ([]uint32, error) {
return uids, nil
}
-func (h handler) fetchThreadIds(uids []uint32) ([]string, error) {
+func (h handler) fetchThreadIds(uids []models.UID) ([]string, error) {
messages := make(chan *imap.Message)
done := make(chan error)
@@ -58,7 +59,9 @@ func (h handler) fetchThreadIds(uids []uint32) ([]string, error) {
}()
var set imap.SeqSet
- set.AddNum(uids...)
+ for _, uid := range uids {
+ set.AddNum(models.UidToUint32(uid))
+ }
err := h.client.UidFetch(&set, items, messages)
<-done
@@ -69,18 +72,18 @@ func (h handler) fetchThreadIds(uids []uint32) ([]string, error) {
return thrid, err
}
-func (h handler) searchUids(thrid []string) ([]uint32, error) {
+func (h handler) searchUids(thrid []string) ([]models.UID, error) {
if len(thrid) == 0 {
return nil, errors.New("no thread IDs provided")
}
return h.runSearch(NewThreadIDSearch(thrid))
}
-func (h handler) RawSearch(rawSearch string) ([]uint32, error) {
+func (h handler) RawSearch(rawSearch string) ([]models.UID, error) {
return h.runSearch(NewRawSearch(rawSearch))
}
-func (h handler) runSearch(cmd imap.Commander) ([]uint32, error) {
+func (h handler) runSearch(cmd imap.Commander) ([]models.UID, error) {
if h.client.State() != imap.SelectedState {
return nil, errors.New("no mailbox selected")
}
@@ -90,5 +93,9 @@ func (h handler) runSearch(cmd imap.Commander) ([]uint32, error) {
if err != nil {
return nil, fmt.Errorf("imap execute failed: %w", err)
}
- return res.Ids, status.Err()
+ var uids []models.UID
+ for _, i := range res.Ids {
+ uids = append(uids, models.Uint32ToUid(i))
+ }
+ return uids, status.Err()
}
diff --git a/worker/imap/fetch.go b/worker/imap/fetch.go
index 202038fe..9b77c777 100644
--- a/worker/imap/fetch.go
+++ b/worker/imap/fetch.go
@@ -82,7 +82,7 @@ func (imapw *IMAPWorker) handleFetchMessageHeaders(
RFC822Headers: header,
Refs: parse.MsgIDList(header, "references"),
Size: _msg.Size,
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
}
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
@@ -126,7 +126,7 @@ func (imapw *IMAPWorker) handleFetchMessageBodyPart(
partHeaderSection.FetchItem(),
partBodySection.FetchItem(),
}
- imapw.handleFetchMessages(msg, []uint32{msg.Uid}, items,
+ imapw.handleFetchMessages(msg, []models.UID{msg.Uid}, items,
func(_msg *imap.Message) error {
if len(_msg.Body) == 0 {
// ignore duplicate messages with only flag updates
@@ -154,7 +154,7 @@ func (imapw *IMAPWorker) handleFetchMessageBodyPart(
Message: types.RespondTo(msg),
Part: &models.MessageBodyPart{
Reader: part.Body,
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
// Update flags (to mark message as read)
@@ -162,7 +162,7 @@ func (imapw *IMAPWorker) handleFetchMessageBodyPart(
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
return nil
@@ -196,7 +196,7 @@ func (imapw *IMAPWorker) handleFetchFullMessages(
Message: types.RespondTo(msg),
Content: &models.FullMessage{
Reader: bufio.NewReader(r),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
// Update flags (to mark message as read)
@@ -204,7 +204,7 @@ func (imapw *IMAPWorker) handleFetchFullMessages(
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
return nil
@@ -228,7 +228,7 @@ func (imapw *IMAPWorker) handleFetchMessageFlags(msg *types.FetchMessageFlags) {
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
return nil
@@ -236,13 +236,13 @@ func (imapw *IMAPWorker) handleFetchMessageFlags(msg *types.FetchMessageFlags) {
}
func (imapw *IMAPWorker) handleFetchMessages(
- msg types.WorkerMessage, uids []uint32, items []imap.FetchItem,
+ msg types.WorkerMessage, uids []models.UID, items []imap.FetchItem,
procFunc func(*imap.Message) error,
) {
messages := make(chan *imap.Message)
done := make(chan struct{})
- missingUids := make(map[uint32]bool)
+ missingUids := make(map[models.UID]bool)
for _, uid := range uids {
missingUids[uid] = true
}
@@ -251,14 +251,14 @@ func (imapw *IMAPWorker) handleFetchMessages(
defer log.PanicHandler()
for _msg := range messages {
- delete(missingUids, _msg.Uid)
+ delete(missingUids, models.Uint32ToUid(_msg.Uid))
err := procFunc(_msg)
if err != nil {
log.Errorf("failed to process message <%d>: %v", _msg.Uid, err)
imapw.worker.PostMessage(&types.MessageInfo{
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
Error: err,
},
}, nil)
diff --git a/worker/imap/flags.go b/worker/imap/flags.go
index 60137bd3..31d3bea3 100644
--- a/worker/imap/flags.go
+++ b/worker/imap/flags.go
@@ -81,7 +81,7 @@ func (imapw *IMAPWorker) handleAnsweredMessages(msg *types.AnsweredMessages) {
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
return nil
@@ -100,7 +100,7 @@ func (imapw *IMAPWorker) handleFlagMessages(msg *types.FlagMessages) {
Message: types.RespondTo(msg),
Info: &models.MessageInfo{
Flags: translateImapFlags(_msg.Flags),
- Uid: _msg.Uid,
+ Uid: models.Uint32ToUid(_msg.Uid),
},
}, nil)
return nil
@@ -108,7 +108,7 @@ func (imapw *IMAPWorker) handleFlagMessages(msg *types.FlagMessages) {
}
func (imapw *IMAPWorker) handleStoreOps(
- msg types.WorkerMessage, uids []uint32, item imap.StoreItem, flag interface{},
+ msg types.WorkerMessage, uids []models.UID, item imap.StoreItem, flag interface{},
procFunc func(*imap.Message) error,
) {
messages := make(chan *imap.Message)
diff --git a/worker/imap/imap.go b/worker/imap/imap.go
index fbdcb2b9..67d56264 100644
--- a/worker/imap/imap.go
+++ b/worker/imap/imap.go
@@ -14,12 +14,12 @@ func init() {
imap.CharsetReader = charset.Reader
}
-func toSeqSet(uids []uint32) *imap.SeqSet {
- var set imap.SeqSet
+func toSeqSet(uids []models.UID) *imap.SeqSet {
+ set := new(imap.SeqSet)
for _, uid := range uids {
- set.AddNum(uid)
+ set.AddNum(models.UidToUint32(uid))
}
- return &set
+ return set
}
func translateBodyStructure(bs *imap.BodyStructure) *models.BodyStructure {
diff --git a/worker/imap/list.go b/worker/imap/list.go
index d12ae4bd..e3c9db3e 100644
--- a/worker/imap/list.go
+++ b/worker/imap/list.go
@@ -139,6 +139,6 @@ func (imapw *IMAPWorker) handleSearchDirectory(msg *types.SearchDirectory) {
imapw.worker.PostMessage(&types.SearchResults{
Message: types.RespondTo(msg),
- Uids: uids,
+ Uids: models.Uint32ToUidList(uids),
}, nil)
}
diff --git a/worker/imap/open.go b/worker/imap/open.go
index 355709a7..b1314a4b 100644
--- a/worker/imap/open.go
+++ b/worker/imap/open.go
@@ -5,6 +5,7 @@ import (
sortthread "github.com/emersion/go-imap-sortthread"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -85,9 +86,10 @@ func (imapw *IMAPWorker) handleFetchDirectoryContents(
// Only initialize if we are not filtering
imapw.seqMap.Initialize(uids)
}
+
imapw.worker.PostMessage(&types.DirectoryContents{
Message: types.RespondTo(msg),
- Uids: uids,
+ Uids: models.Uint32ToUidList(uids),
}, nil)
imapw.worker.PostMessage(&types.Done{Message: types.RespondTo(msg)}, nil)
}
@@ -146,7 +148,7 @@ func (imapw *IMAPWorker) handleDirectoryThreaded(
var uids []uint32
for i := len(aercThreads) - 1; i >= 0; i-- {
aercThreads[i].Walk(func(t *types.Thread, level int, currentErr error) error { //nolint:errcheck // error indicates skipped threads
- uids = append(uids, t.Uid)
+ uids = append(uids, models.UidToUint32(t.Uid))
return nil
})
}
@@ -175,7 +177,7 @@ func convertThreads(threads []*sortthread.Thread, parent *types.Thread) ([]*type
for i := 0; i < len(threads); i++ {
t := threads[i]
conv[i] = &types.Thread{
- Uid: t.Id,
+ Uid: models.Uint32ToUid(t.Id),
}
// Set the first child node
diff --git a/worker/imap/worker.go b/worker/imap/worker.go
index edb68ec4..28eb907c 100644
--- a/worker/imap/worker.go
+++ b/worker/imap/worker.go
@@ -289,7 +289,7 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) {
Envelope: translateEnvelope(msg.Envelope),
Flags: translateImapFlags(msg.Flags),
InternalDate: msg.InternalDate,
- Uid: msg.Uid,
+ Uid: models.Uint32ToUid(msg.Uid),
},
}, nil)
case *client.ExpungeUpdate:
@@ -297,7 +297,7 @@ func (w *IMAPWorker) handleImapUpdate(update client.Update) {
w.worker.Errorf("ExpungeUpdate unknown seqnum: %d", update.SeqNum)
} else {
w.worker.PostMessage(&types.MessagesDeleted{
- Uids: []uint32{uid},
+ Uids: []models.UID{models.Uint32ToUid(uid)},
}, nil)
}
}
diff --git a/worker/jmap/directories.go b/worker/jmap/directories.go
index 07bb0762..7ae79ead 100644
--- a/worker/jmap/directories.go
+++ b/worker/jmap/directories.go
@@ -186,9 +186,9 @@ func (w *JMAPWorker) handleFetchDirectoryContents(msg *types.FetchDirectoryConte
}
}
- uids := make([]uint32, 0, len(contents.MessageIDs))
+ uids := make([]models.UID, 0, len(contents.MessageIDs))
for _, id := range contents.MessageIDs {
- uids = append(uids, w.uidStore.GetOrInsert(string(id)))
+ uids = append(uids, models.UID(id))
}
w.w.PostMessage(&types.DirectoryContents{
Message: types.RespondTo(msg),
@@ -214,9 +214,9 @@ func (w *JMAPWorker) handleSearchDirectory(msg *types.SearchDirectory) error {
for _, inv := range resp.Responses {
switch r := inv.Args.(type) {
case *email.QueryResponse:
- var uids []uint32
+ var uids []models.UID
for _, id := range r.IDs {
- uids = append(uids, w.uidStore.GetOrInsert(string(id)))
+ uids = append(uids, models.UID(id))
}
w.w.PostMessage(&types.SearchResults{
Message: types.RespondTo(msg),
diff --git a/worker/jmap/fetch.go b/worker/jmap/fetch.go
index 07579b99..3b3a8460 100644
--- a/worker/jmap/fetch.go
+++ b/worker/jmap/fetch.go
@@ -38,11 +38,7 @@ func (w *JMAPWorker) handleFetchMessageHeaders(msg *types.FetchMessageHeaders) e
emailIdsToFetch := make([]jmap.ID, 0, len(msg.Uids))
currentEmails := make([]*email.Email, 0, len(msg.Uids))
for _, uid := range msg.Uids {
- id, ok := w.uidStore.GetKey(uid)
- if !ok {
- return fmt.Errorf("bug: no jmap id for message uid: %v", uid)
- }
- jid := jmap.ID(id)
+ jid := jmap.ID(uid)
m, err := w.cache.GetEmail(jid)
if err != nil {
// Message wasn't in cache; fetch it
@@ -103,13 +99,9 @@ func (w *JMAPWorker) handleFetchMessageHeaders(msg *types.FetchMessageHeaders) e
}
func (w *JMAPWorker) handleFetchMessageBodyPart(msg *types.FetchMessageBodyPart) error {
- id, ok := w.uidStore.GetKey(msg.Uid)
- if !ok {
- return fmt.Errorf("bug: unknown message uid %d", msg.Uid)
- }
- mail, err := w.cache.GetEmail(jmap.ID(id))
+ mail, err := w.cache.GetEmail(jmap.ID(msg.Uid))
if err != nil {
- return fmt.Errorf("bug: unknown message id %s: %w", id, err)
+ return fmt.Errorf("bug: unknown message id %s: %w", msg.Uid, err)
}
part := mail.BodyStructure
@@ -159,13 +151,9 @@ func (w *JMAPWorker) handleFetchMessageBodyPart(msg *types.FetchMessageBodyPart)
func (w *JMAPWorker) handleFetchFullMessages(msg *types.FetchFullMessages) error {
for _, uid := range msg.Uids {
- id, ok := w.uidStore.GetKey(uid)
- if !ok {
- return fmt.Errorf("bug: unknown message uid %d", uid)
- }
- mail, err := w.cache.GetEmail(jmap.ID(id))
+ mail, err := w.cache.GetEmail(jmap.ID(uid))
if err != nil {
- return fmt.Errorf("bug: unknown message id %s: %w", id, err)
+ return fmt.Errorf("bug: unknown message id %s: %w", uid, err)
}
buf, err := w.cache.GetBlob(mail.BlobID)
if err != nil {
diff --git a/worker/jmap/jmap.go b/worker/jmap/jmap.go
index bb0f75f9..7320ec04 100644
--- a/worker/jmap/jmap.go
+++ b/worker/jmap/jmap.go
@@ -37,7 +37,7 @@ func (w *JMAPWorker) translateMsgInfo(m *email.Email) *models.MessageInfo {
return &models.MessageInfo{
Envelope: env,
Flags: keywordsToFlags(m.Keywords),
- Uid: w.uidStore.GetOrInsert(string(m.ID)),
+ Uid: models.UID(m.ID),
BodyStructure: translateBodyStructure(m.BodyStructure),
RFC822Headers: translateJMAPHeader(m.Headers),
Refs: m.References,
diff --git a/worker/jmap/push.go b/worker/jmap/push.go
index bc90dd77..f2a8ad45 100644
--- a/worker/jmap/push.go
+++ b/worker/jmap/push.go
@@ -247,10 +247,9 @@ func (w *JMAPWorker) refresh(newState jmap.TypeState) error {
}
if w.selectedMbox == mboxId {
- uids := make([]uint32, 0, len(ids))
+ uids := make([]models.UID, 0, len(ids))
for _, id := range ids {
- uid := w.uidStore.GetOrInsert(string(id))
- uids = append(uids, uid)
+ uids = append(uids, models.UID(id))
}
w.w.PostMessage(&types.DirectoryContents{
Uids: uids,
diff --git a/worker/jmap/set.go b/worker/jmap/set.go
index 36b23688..302314c1 100644
--- a/worker/jmap/set.go
+++ b/worker/jmap/set.go
@@ -10,15 +10,11 @@ import (
"git.sr.ht/~rockorager/go-jmap/mail/mailbox"
)
-func (w *JMAPWorker) updateFlags(uids []uint32, flags models.Flags, enable bool) error {
+func (w *JMAPWorker) updateFlags(uids []models.UID, flags models.Flags, enable bool) error {
var req jmap.Request
patches := make(map[jmap.ID]jmap.Patch)
for _, uid := range uids {
- id, ok := w.uidStore.GetKey(uid)
- if !ok {
- return fmt.Errorf("bug: unknown uid %d", uid)
- }
patch := jmap.Patch{}
for kw := range flagsToKeywords(flags) {
path := fmt.Sprintf("keywords/%s", kw)
@@ -28,7 +24,7 @@ func (w *JMAPWorker) updateFlags(uids []uint32, flags models.Flags, enable bool)
patch[path] = nil
}
}
- patches[jmap.ID(id)] = patch
+ patches[jmap.ID(uid)] = patch
}
req.Invoke(&email.Set{
@@ -44,7 +40,7 @@ func (w *JMAPWorker) updateFlags(uids []uint32, flags models.Flags, enable bool)
return checkNotUpdated(resp)
}
-func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, deleteSrc bool) error {
+func (w *JMAPWorker) moveCopy(uids []models.UID, destDir string, deleteSrc bool) error {
var req jmap.Request
var destMbox jmap.ID
var destroy []jmap.ID
@@ -62,13 +58,9 @@ func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, deleteSrc bool) err
for _, uid := range uids {
dest := destMbox
- id, ok := w.uidStore.GetKey(uid)
- if !ok {
- return fmt.Errorf("bug: unknown uid %d", uid)
- }
- mail, err := w.cache.GetEmail(jmap.ID(id))
+ mail, err := w.cache.GetEmail(jmap.ID(uid))
if err != nil {
- return fmt.Errorf("bug: unknown message id %s: %w", id, err)
+ return fmt.Errorf("bug: unknown message id %s: %w", uid, err)
}
patch := w.moveCopyPatch(mail, dest, deleteSrc)
@@ -76,7 +68,7 @@ func (w *JMAPWorker) moveCopy(uids []uint32, destDir string, deleteSrc bool) err
destroy = append(destroy, mail.ID)
w.w.Debugf("destroying <%s>", mail.MessageID[0])
} else {
- patches[jmap.ID(id)] = patch
+ patches[jmap.ID(uid)] = patch
}
}
@@ -161,11 +153,7 @@ func (w *JMAPWorker) handleModifyLabels(msg *types.ModifyLabels) error {
patches := make(map[jmap.ID]jmap.Patch)
for _, uid := range msg.Uids {
- id, ok := w.uidStore.GetKey(uid)
- if !ok {
- return fmt.Errorf("bug: unknown uid %d", uid)
- }
- patches[jmap.ID(id)] = patch
+ patches[jmap.ID(uid)] = patch
}
req.Invoke(&email.Set{
diff --git a/worker/jmap/worker.go b/worker/jmap/worker.go
index 67553272..58883e1e 100644
--- a/worker/jmap/worker.go
+++ b/worker/jmap/worker.go
@@ -6,7 +6,6 @@ import (
"time"
"git.sr.ht/~rjarry/aerc/config"
- "git.sr.ht/~rjarry/aerc/lib/uidstore"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/handlers"
"git.sr.ht/~rjarry/aerc/worker/jmap/cache"
@@ -47,7 +46,6 @@ type JMAPWorker struct {
mbox2dir map[jmap.ID]string
roles map[mailbox.Role]jmap.ID
identities map[string]*identity.Identity
- uidStore *uidstore.Store
changes chan jmap.TypeState
stop chan struct{}
@@ -56,7 +54,6 @@ type JMAPWorker struct {
func NewJMAPWorker(worker *types.Worker) (types.Backend, error) {
return &JMAPWorker{
w: worker,
- uidStore: uidstore.NewStore(),
roles: make(map[mailbox.Role]jmap.ID),
dir2mbox: make(map[string]jmap.ID),
mbox2dir: make(map[jmap.ID]string),
diff --git a/worker/lib/search.go b/worker/lib/search.go
index d44e2ce1..b98e2bbb 100644
--- a/worker/lib/search.go
+++ b/worker/lib/search.go
@@ -13,11 +13,11 @@ import (
"git.sr.ht/~rjarry/go-opt"
)
-func Search(messages []rfc822.RawMessage, criteria *types.SearchCriteria) ([]uint32, error) {
+func Search(messages []rfc822.RawMessage, criteria *types.SearchCriteria) ([]models.UID, error) {
criteria.PrepareHeader()
requiredParts := GetRequiredParts(criteria)
- matchedUids := []uint32{}
+ var matchedUids []models.UID
for _, m := range messages {
success, err := SearchMessage(m, criteria, requiredParts)
if err != nil {
diff --git a/worker/lib/sort.go b/worker/lib/sort.go
index 7af2c6fa..70a64c7b 100644
--- a/worker/lib/sort.go
+++ b/worker/lib/sort.go
@@ -11,7 +11,7 @@ import (
func Sort(messageInfos []*models.MessageInfo,
criteria []*types.SortCriterion,
-) ([]uint32, error) {
+) ([]models.UID, 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]
@@ -56,7 +56,7 @@ func Sort(messageInfos []*models.MessageInfo,
})
}
}
- var uids []uint32
+ var uids []models.UID
// copy in reverse as msgList displays backwards
for i := len(messageInfos) - 1; i >= 0; i-- {
uids = append(uids, messageInfos[i].Uid)
diff --git a/worker/maildir/container.go b/worker/maildir/container.go
index dea1ded6..c23825da 100644
--- a/worker/maildir/container.go
+++ b/worker/maildir/container.go
@@ -9,7 +9,7 @@ import (
"github.com/emersion/go-maildir"
"git.sr.ht/~rjarry/aerc/lib/log"
- "git.sr.ht/~rjarry/aerc/lib/uidstore"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/lib"
)
@@ -17,8 +17,7 @@ import (
// the Maildir spec
type Container struct {
Store *lib.MaildirStore
- uids *uidstore.Store
- recentUIDS map[uint32]struct{} // used to set the recent flag
+ recentUIDS map[models.UID]struct{} // used to set the recent flag
}
// NewContainer creates a new container at the specified directory
@@ -28,8 +27,8 @@ func NewContainer(dir string, maildirpp bool) (*Container, error) {
return nil, err
}
return &Container{
- Store: store, uids: uidstore.NewStore(),
- recentUIDS: make(map[uint32]struct{}),
+ Store: store,
+ recentUIDS: make(map[models.UID]struct{}),
}, nil
}
@@ -40,8 +39,7 @@ func (c *Container) SyncNewMail(dir maildir.Dir) error {
return err
}
for _, key := range keys {
- uid := c.uids.GetOrInsert(key)
- c.recentUIDS[uid] = struct{}{}
+ c.recentUIDS[models.UID(key)] = struct{}{}
}
return nil
}
@@ -57,18 +55,18 @@ func (c *Container) OpenDirectory(name string) (maildir.Dir, error) {
}
// IsRecent returns if a uid has the Recent flag set
-func (c *Container) IsRecent(uid uint32) bool {
+func (c *Container) IsRecent(uid models.UID) bool {
_, ok := c.recentUIDS[uid]
return ok
}
// ClearRecentFlag removes the Recent flag from the message with the given uid
-func (c *Container) ClearRecentFlag(uid uint32) {
+func (c *Container) ClearRecentFlag(uid models.UID) {
delete(c.recentUIDS, uid)
}
// UIDs fetches the unique message identifiers for the maildir
-func (c *Container) UIDs(d maildir.Dir) ([]uint32, error) {
+func (c *Container) UIDs(d maildir.Dir) ([]models.UID, error) {
keys, err := d.Keys()
if err != nil && len(keys) == 0 {
return nil, fmt.Errorf("could not get keys for %s: %w", d, err)
@@ -77,39 +75,26 @@ func (c *Container) UIDs(d maildir.Dir) ([]uint32, error) {
log.Errorf("could not get all keys for %s: %s", d, err.Error())
}
sort.Strings(keys)
- var uids []uint32
+ var uids []models.UID
for _, key := range keys {
- uids = append(uids, c.uids.GetOrInsert(key))
+ uids = append(uids, models.UID(key))
}
return uids, err
}
// Message returns a Message struct for the given UID and maildir
-func (c *Container) Message(d maildir.Dir, uid uint32) (*Message, error) {
- if key, ok := c.uids.GetKey(uid); ok {
- return &Message{
- dir: d,
- uid: uid,
- key: key,
- }, nil
- }
- return nil, fmt.Errorf("could not find message with uid %d in maildir %s",
- uid, d)
-}
-
-func (c *Container) MessageFromKey(d maildir.Dir, key string) *Message {
- uid := c.uids.GetOrInsert(key)
+func (c *Container) Message(d maildir.Dir, uid models.UID) (*Message, error) {
return &Message{
dir: d,
uid: uid,
- key: key,
- }
+ key: string(uid),
+ }, nil
}
// DeleteAll deletes a set of messages by UID and returns the subset of UIDs
// which were successfully deleted, stopping upon the first error.
-func (c *Container) DeleteAll(d maildir.Dir, uids []uint32) ([]uint32, error) {
- var success []uint32
+func (c *Container) DeleteAll(d maildir.Dir, uids []models.UID) ([]models.UID, error) {
+ var success []models.UID
for _, uid := range uids {
msg, err := c.Message(d, uid)
if err != nil {
@@ -124,46 +109,38 @@ func (c *Container) DeleteAll(d maildir.Dir, uids []uint32) ([]uint32, error) {
}
func (c *Container) CopyAll(
- dest maildir.Dir, src maildir.Dir, uids []uint32,
+ dest maildir.Dir, src maildir.Dir, uids []models.UID,
) error {
for _, uid := range uids {
if err := c.copyMessage(dest, src, uid); err != nil {
- return fmt.Errorf("could not copy message %d: %w", uid, err)
+ return fmt.Errorf("could not copy message %s: %w", uid, err)
}
}
return nil
}
func (c *Container) copyMessage(
- dest maildir.Dir, src maildir.Dir, uid uint32,
+ dest maildir.Dir, src maildir.Dir, uid models.UID,
) error {
- key, ok := c.uids.GetKey(uid)
- if !ok {
- return fmt.Errorf("could not find key for message id %d", uid)
- }
- _, err := src.Copy(dest, key)
+ _, err := src.Copy(dest, string(uid))
return err
}
-func (c *Container) MoveAll(dest maildir.Dir, src maildir.Dir, uids []uint32) ([]uint32, error) {
- var success []uint32
+func (c *Container) MoveAll(dest maildir.Dir, src maildir.Dir, uids []models.UID) ([]models.UID, error) {
+ var success []models.UID
for _, uid := range uids {
if err := c.moveMessage(dest, src, uid); err != nil {
- return success, fmt.Errorf("could not move message %d: %w", uid, err)
+ return success, fmt.Errorf("could not move message %s: %w", uid, err)
}
success = append(success, uid)
}
return success, nil
}
-func (c *Container) moveMessage(dest maildir.Dir, src maildir.Dir, uid uint32) error {
- key, ok := c.uids.GetKey(uid)
- if !ok {
- return fmt.Errorf("could not find key for message id %d", uid)
- }
- path, err := src.Filename(key)
+func (c *Container) moveMessage(dest maildir.Dir, src maildir.Dir, uid models.UID) error {
+ path, err := src.Filename(string(uid))
if err != nil {
- return fmt.Errorf("could not find path for message id %d", uid)
+ return fmt.Errorf("could not find path for message id %s: %w", uid, err)
}
// Remove encoded UID information from the key to prevent sync issues
name := lib.StripUIDFromMessageFilename(filepath.Base(path))
diff --git a/worker/maildir/message.go b/worker/maildir/message.go
index 1d8d26b9..14c83721 100644
--- a/worker/maildir/message.go
+++ b/worker/maildir/message.go
@@ -15,7 +15,7 @@ import (
// A Message is an individual email inside of a maildir.Dir.
type Message struct {
dir maildir.Dir
- uid uint32
+ uid models.UID
key string
}
@@ -135,7 +135,7 @@ func (m Message) NewBodyPartReader(requestedParts []int) (io.Reader, error) {
return rfc822.FetchEntityPartReader(msg, requestedParts)
}
-func (m Message) UID() uint32 {
+func (m Message) UID() models.UID {
return m.uid
}
diff --git a/worker/maildir/search.go b/worker/maildir/search.go
index 90c84087..de2d0530 100644
--- a/worker/maildir/search.go
+++ b/worker/maildir/search.go
@@ -6,11 +6,12 @@ import (
"sync"
"git.sr.ht/~rjarry/aerc/lib/log"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/lib"
"git.sr.ht/~rjarry/aerc/worker/types"
)
-func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]uint32, error) {
+func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]models.UID, error) {
criteria.PrepareHeader()
requiredParts := lib.GetRequiredParts(criteria)
w.worker.Debugf("Required parts bitmask for search: %b", requiredParts)
@@ -20,7 +21,7 @@ func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]
return nil, err
}
- matchedUids := []uint32{}
+ var matchedUids []models.UID
mu := sync.Mutex{}
wg := sync.WaitGroup{}
// Hard limit at 2x CPU cores
@@ -33,7 +34,7 @@ func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]
default:
limit <- struct{}{}
wg.Add(1)
- go func(key uint32) {
+ go func(key models.UID) {
defer log.PanicHandler()
defer wg.Done()
success, err := w.searchKey(key, criteria, requiredParts)
@@ -55,7 +56,7 @@ func (w *Worker) search(ctx context.Context, criteria *types.SearchCriteria) ([]
}
// Execute the search criteria for the given key, returns true if search succeeded
-func (w *Worker) searchKey(key uint32, criteria *types.SearchCriteria,
+func (w *Worker) searchKey(key models.UID, criteria *types.SearchCriteria,
parts lib.MsgParts,
) (bool, error) {
message, err := w.c.Message(*w.selected, key)
diff --git a/worker/maildir/worker.go b/worker/maildir/worker.go
index 2c5bc893..74efdc3c 100644
--- a/worker/maildir/worker.go
+++ b/worker/maildir/worker.go
@@ -460,7 +460,7 @@ func (w *Worker) handleFetchDirectoryContents(
msg *types.FetchDirectoryContents,
) error {
var (
- uids []uint32
+ uids []models.UID
err error
)
if msg.Filter != nil {
@@ -494,7 +494,7 @@ func (w *Worker) handleFetchDirectoryContents(
return nil
}
-func (w *Worker) sort(ctx context.Context, uids []uint32, criteria []*types.SortCriterion) ([]uint32, error) {
+func (w *Worker) sort(ctx context.Context, uids []models.UID, criteria []*types.SortCriterion) ([]models.UID, error) {
if len(criteria) == 0 {
// At least sort by uid, parallel searching can create random
// order
@@ -516,7 +516,7 @@ func (w *Worker) sort(ctx context.Context, uids []uint32, criteria []*types.Sort
default:
limit <- struct{}{}
wg.Add(1)
- go func(uid uint32) {
+ go func(uid models.UID) {
defer log.PanicHandler()
defer wg.Done()
info, err := w.msgHeadersFromUid(uid)
@@ -546,7 +546,7 @@ func (w *Worker) handleFetchDirectoryThreaded(
msg *types.FetchDirectoryThreaded,
) error {
var (
- uids []uint32
+ uids []models.UID
err error
)
if msg.Filter != nil {
@@ -574,7 +574,7 @@ func (w *Worker) handleFetchDirectoryThreaded(
return nil
}
-func (w *Worker) threads(ctx context.Context, uids []uint32,
+func (w *Worker) threads(ctx context.Context, uids []models.UID,
criteria []*types.SortCriterion,
) ([]*types.Thread, error) {
builder := aercLib.NewThreadBuilder(iterator.NewFactory(false), false)
@@ -590,7 +590,7 @@ func (w *Worker) threads(ctx context.Context, uids []uint32,
default:
limit <- struct{}{}
wg.Add(1)
- go func(uid uint32) {
+ go func(uid models.UID) {
defer log.PanicHandler()
defer wg.Done()
info, err := w.msgHeadersFromUid(uid)
@@ -903,7 +903,7 @@ func (w *Worker) handleSearchDirectory(msg *types.SearchDirectory) error {
return nil
}
-func (w *Worker) msgInfoFromUid(uid uint32) (*models.MessageInfo, error) {
+func (w *Worker) msgInfoFromUid(uid models.UID) (*models.MessageInfo, error) {
m, err := w.c.Message(*w.selected, uid)
if err != nil {
return nil, err
@@ -923,7 +923,7 @@ func (w *Worker) msgInfoFromUid(uid uint32) (*models.MessageInfo, error) {
return info, nil
}
-func (w *Worker) msgHeadersFromUid(uid uint32) (*models.MessageInfo, error) {
+func (w *Worker) msgHeadersFromUid(uid models.UID) (*models.MessageInfo, error) {
m, err := w.c.Message(*w.selected, uid)
if err != nil {
return nil, err
diff --git a/worker/mbox/io.go b/worker/mbox/io.go
index f5fbc596..22d0d023 100644
--- a/worker/mbox/io.go
+++ b/worker/mbox/io.go
@@ -12,7 +12,6 @@ import (
func Read(r io.Reader) ([]rfc822.RawMessage, error) {
mbr := mbox.NewReader(r)
- uid := uint32(0)
messages := make([]rfc822.RawMessage, 0)
for {
msg, err := mbr.NextMessage()
@@ -28,10 +27,10 @@ func Read(r io.Reader) ([]rfc822.RawMessage, error) {
}
messages = append(messages, &message{
- uid: uid, flags: models.SeenFlag, content: content,
+ uid: uidFromContents(content),
+ flags: models.SeenFlag,
+ content: content,
})
-
- uid++
}
return messages, nil
}
diff --git a/worker/mbox/models.go b/worker/mbox/models.go
index 5acd6f5a..ebfe5d32 100644
--- a/worker/mbox/models.go
+++ b/worker/mbox/models.go
@@ -2,6 +2,8 @@ package mboxer
import (
"bytes"
+ "crypto/sha256"
+ "encoding/hex"
"fmt"
"io"
@@ -49,7 +51,7 @@ func (md *mailboxContainer) DirectoryInfo(file string) *models.DirectoryInfo {
}
}
-func (md *mailboxContainer) Copy(dest, src string, uids []uint32) error {
+func (md *mailboxContainer) Copy(dest, src string, uids []models.UID) error {
srcmbox, ok := md.Mailbox(src)
if !ok {
return fmt.Errorf("source %s not found", src)
@@ -69,15 +71,15 @@ func (md *mailboxContainer) Copy(dest, src string, uids []uint32) error {
if found {
msg, err := srcmbox.Message(uidSrc)
if err != nil {
- return fmt.Errorf("could not get message with uid %d from folder %s", uidSrc, src)
+ return fmt.Errorf("could not get message with uid %s from folder %s", uidSrc, src)
}
r, err := msg.NewReader()
if err != nil {
- return fmt.Errorf("could not get reader for message with uid %d", uidSrc)
+ return fmt.Errorf("could not get reader for message with uid %s", uidSrc)
}
flags, err := msg.ModelFlags()
if err != nil {
- return fmt.Errorf("could not get flags for message with uid %d", uidSrc)
+ return fmt.Errorf("could not get flags for message with uid %s", uidSrc)
}
err = destmbox.Append(r, flags)
if err != nil {
@@ -94,24 +96,24 @@ type container struct {
messages []rfc822.RawMessage
}
-func (f *container) Uids() []uint32 {
- uids := make([]uint32, len(f.messages))
+func (f *container) Uids() []models.UID {
+ uids := make([]models.UID, len(f.messages))
for i, m := range f.messages {
uids[i] = m.UID()
}
return uids
}
-func (f *container) Message(uid uint32) (rfc822.RawMessage, error) {
+func (f *container) Message(uid models.UID) (rfc822.RawMessage, error) {
for _, m := range f.messages {
if uid == m.UID() {
return m, nil
}
}
- return &message{}, fmt.Errorf("uid [%d] not found", uid)
+ return &message{}, fmt.Errorf("uid [%s] not found", uid)
}
-func (f *container) Delete(uids []uint32) (deleted []uint32) {
+func (f *container) Delete(uids []models.UID) (deleted []models.UID) {
newMessages := make([]rfc822.RawMessage, 0)
for _, m := range f.messages {
del := false
@@ -131,32 +133,28 @@ func (f *container) Delete(uids []uint32) (deleted []uint32) {
return
}
-func (f *container) newUid() (next uint32) {
- for _, m := range f.messages {
- if uid := m.UID(); uid > next {
- next = uid
- }
- }
- next++
- return
-}
-
func (f *container) Append(r io.Reader, flags models.Flags) error {
data, err := io.ReadAll(r)
if err != nil {
return err
}
f.messages = append(f.messages, &message{
- uid: f.newUid(),
+ uid: uidFromContents(data),
flags: flags,
content: data,
})
return nil
}
+func uidFromContents(data []byte) models.UID {
+ sum := sha256.New()
+ sum.Write(data)
+ return models.UID(hex.EncodeToString(sum.Sum(nil)))
+}
+
// message implements the lib.RawMessage interface
type message struct {
- uid uint32
+ uid models.UID
flags models.Flags
content []byte
}
@@ -173,7 +171,7 @@ func (m *message) Labels() ([]string, error) {
return nil, nil
}
-func (m *message) UID() uint32 {
+func (m *message) UID() models.UID {
return m.uid
}
diff --git a/worker/mbox/worker.go b/worker/mbox/worker.go
index 1f4a4965..72a44368 100644
--- a/worker/mbox/worker.go
+++ b/worker/mbox/worker.go
@@ -405,7 +405,7 @@ func (w *mboxWorker) PathSeparator() string {
return "/"
}
-func filterUids(folder *container, uids []uint32, criteria *types.SearchCriteria) ([]uint32, error) {
+func filterUids(folder *container, uids []models.UID, criteria *types.SearchCriteria) ([]models.UID, error) {
log.Debugf("Search with parsed criteria: %#v", criteria)
m := make([]rfc822.RawMessage, 0, len(uids))
for _, uid := range uids {
@@ -419,9 +419,9 @@ func filterUids(folder *container, uids []uint32, criteria *types.SearchCriteria
return lib.Search(m, criteria)
}
-func sortUids(folder *container, uids []uint32,
+func sortUids(folder *container, uids []models.UID,
criteria []*types.SortCriterion,
-) ([]uint32, error) {
+) ([]models.UID, error) {
var infos []*models.MessageInfo
needSize := false
for _, item := range criteria {
diff --git a/worker/notmuch/lib/database.go b/worker/notmuch/lib/database.go
index 9a8a9a60..2915605d 100644
--- a/worker/notmuch/lib/database.go
+++ b/worker/notmuch/lib/database.go
@@ -10,7 +10,7 @@ import (
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/lib/notmuch"
- "git.sr.ht/~rjarry/aerc/lib/uidstore"
+ "git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -18,7 +18,6 @@ type DB struct {
path string
excludedTags []string
db *notmuch.Database
- uidStore *uidstore.Store
}
func NewDB(path string, excludedTags []string) *DB {
@@ -28,7 +27,6 @@ func NewDB(path string, excludedTags []string) *DB {
db := &DB{
path: path,
excludedTags: excludedTags,
- uidStore: uidstore.NewStore(),
db: nm,
}
return db
@@ -312,14 +310,6 @@ func (db *DB) MsgModifyTags(key string, add, remove []string) error {
return msg.SyncTagsToMaildirFlags()
}
-func (db *DB) UidFromKey(key string) uint32 {
- return db.uidStore.GetOrInsert(key)
-}
-
-func (db *DB) KeyFromUid(uid uint32) (string, bool) {
- return db.uidStore.GetKey(uid)
-}
-
func (db *DB) makeThread(parent *types.Thread, msgs *notmuch.Messages, threadContext bool) []*types.Thread {
var siblings []*types.Thread
for msgs.Next() {
@@ -338,7 +328,7 @@ func (db *DB) makeThread(parent *types.Thread, msgs *notmuch.Messages, threadCon
continue
}
node := &types.Thread{
- Uid: db.uidStore.GetOrInsert(msgID),
+ Uid: models.UID(msgID),
Parent: parent,
}
switch threadContext {
diff --git a/worker/notmuch/message.go b/worker/notmuch/message.go
index 539d85ae..81a4da54 100644
--- a/worker/notmuch/message.go
+++ b/worker/notmuch/message.go
@@ -21,7 +21,7 @@ import (
)
type Message struct {
- uid uint32
+ uid models.UID
key string
db *notmuch.DB
}
@@ -152,7 +152,7 @@ func (m *Message) ModelFlags() (models.Flags, error) {
return flags, nil
}
-func (m *Message) UID() uint32 {
+func (m *Message) UID() models.UID {
return m.uid
}
diff --git a/worker/notmuch/worker.go b/worker/notmuch/worker.go
index 06c0cde6..8c954a61 100644
--- a/worker/notmuch/worker.go
+++ b/worker/notmuch/worker.go
@@ -457,27 +457,21 @@ func (w *worker) handleFetchMessageHeaders(
return nil
}
-func (w *worker) uidsFromQuery(ctx context.Context, query string) ([]uint32, error) {
+func (w *worker) uidsFromQuery(ctx context.Context, query string) ([]models.UID, error) {
msgIDs, err := w.db.MsgIDsFromQuery(ctx, query)
if err != nil {
return nil, err
}
- var uids []uint32
+ var uids []models.UID
for _, id := range msgIDs {
- uid := w.db.UidFromKey(id)
- uids = append(uids, uid)
-
+ uids = append(uids, models.UID(id))
}
return uids, nil
}
-func (w *worker) msgFromUid(uid uint32) (*Message, error) {
- key, ok := w.db.KeyFromUid(uid)
- if !ok {
- return nil, fmt.Errorf("Invalid uid: %v", uid)
- }
+func (w *worker) msgFromUid(uid models.UID) (*Message, error) {
msg := &Message{
- key: key,
+ key: string(uid),
uid: uid,
db: w.db,
}
@@ -613,7 +607,7 @@ func (w *worker) handleModifyLabels(msg *types.ModifyLabels) error {
for _, uid := range msg.Uids {
m, err := w.msgFromUid(uid)
if err != nil {
- return fmt.Errorf("could not get message from uid %d: %w", uid, err)
+ return fmt.Errorf("could not get message from uid %s: %w", uid, err)
}
err = m.ModifyTags(msg.Add, msg.Remove)
if err != nil {
@@ -699,7 +693,7 @@ func (w *worker) emitDirectoryThreaded(parent types.WorkerMessage) error {
return nil
}
-func (w *worker) emitMessageInfoError(msg types.WorkerMessage, uid uint32, err error) {
+func (w *worker) emitMessageInfoError(msg types.WorkerMessage, uid models.UID, err error) {
w.w.PostMessage(&types.MessageInfo{
Info: &models.MessageInfo{
Envelope: &models.Envelope{},
@@ -743,9 +737,9 @@ func (w *worker) emitLabelList() {
w.w.PostMessage(&types.LabelList{Labels: tags}, nil)
}
-func (w *worker) sort(uids []uint32,
+func (w *worker) sort(uids []models.UID,
criteria []*types.SortCriterion,
-) ([]uint32, error) {
+) ([]models.UID, error) {
if len(criteria) == 0 {
return uids, nil
}
@@ -796,7 +790,7 @@ func (w *worker) handleDeleteMessages(msg *types.DeleteMessages) error {
return errUnsupported
}
- var deleted []uint32
+ var deleted []models.UID
folders, _ := w.store.FolderMap()
curDir := folders[w.currentQueryName]
@@ -874,7 +868,7 @@ func (w *worker) handleMoveMessages(msg *types.MoveMessages) error {
return errUnsupported
}
- var moved []uint32
+ var moved []models.UID
folders, _ := w.store.FolderMap()
diff --git a/worker/types/messages.go b/worker/types/messages.go
index b91adbb7..0d0a187a 100644
--- a/worker/types/messages.go
+++ b/worker/types/messages.go
@@ -146,29 +146,29 @@ type RemoveDirectory struct {
type FetchMessageHeaders struct {
Message
Context context.Context
- Uids []uint32
+ Uids []models.UID
}
type FetchFullMessages struct {
Message
- Uids []uint32
+ Uids []models.UID
}
type FetchMessageBodyPart struct {
Message
- Uid uint32
+ Uid models.UID
Part []int
}
type FetchMessageFlags struct {
Message
Context context.Context
- Uids []uint32
+ Uids []models.UID
}
type DeleteMessages struct {
Message
- Uids []uint32
+ Uids []models.UID
MultiFileStrategy *MultiFileStrategy
}
@@ -177,32 +177,32 @@ type FlagMessages struct {
Message
Enable bool
Flags models.Flags
- Uids []uint32
+ Uids []models.UID
}
type AnsweredMessages struct {
Message
Answered bool
- Uids []uint32
+ Uids []models.UID
}
type ForwardedMessages struct {
Message
Forwarded bool
- Uids []uint32
+ Uids []models.UID
}
type CopyMessages struct {
Message
Destination string
- Uids []uint32
+ Uids []models.UID
MultiFileStrategy *MultiFileStrategy
}
type MoveMessages struct {
Message
Destination string
- Uids []uint32
+ Uids []models.UID
MultiFileStrategy *MultiFileStrategy
}
@@ -244,12 +244,12 @@ type DirectoryInfo struct {
type DirectoryContents struct {
Message
- Uids []uint32
+ Uids []models.UID
}
type SearchResults struct {
Message
- Uids []uint32
+ Uids []models.UID
}
type MessageInfo struct {
@@ -270,24 +270,24 @@ type MessageBodyPart struct {
type MessagesDeleted struct {
Message
- Uids []uint32
+ Uids []models.UID
}
type MessagesCopied struct {
Message
Destination string
- Uids []uint32
+ Uids []models.UID
}
type MessagesMoved struct {
Message
Destination string
- Uids []uint32
+ Uids []models.UID
}
type ModifyLabels struct {
Message
- Uids []uint32
+ Uids []models.UID
Add []string
Remove []string
}
diff --git a/worker/types/thread.go b/worker/types/thread.go
index 42565964..fe6c56bf 100644
--- a/worker/types/thread.go
+++ b/worker/types/thread.go
@@ -6,10 +6,11 @@ import (
"sort"
"git.sr.ht/~rjarry/aerc/lib/log"
+ "git.sr.ht/~rjarry/aerc/models"
)
type Thread struct {
- Uid uint32
+ Uid models.UID
Parent *Thread
PrevSibling *Thread
NextSibling *Thread
@@ -77,11 +78,11 @@ func (t *Thread) Root() *Thread {
}
// Uids returns all associated uids for the given thread and its children
-func (t *Thread) Uids() []uint32 {
+func (t *Thread) Uids() []models.UID {
if t == nil {
return nil
}
- uids := make([]uint32, 0)
+ uids := make([]models.UID, 0)
err := t.Walk(func(node *Thread, _ int, _ error) error {
uids = append(uids, node.Uid)
return nil
@@ -96,20 +97,20 @@ func (t *Thread) String() string {
if t == nil {
return "<nil>"
}
- parent := -1
+ var parent models.UID
if t.Parent != nil {
- parent = int(t.Parent.Uid)
+ parent = t.Parent.Uid
}
- next := -1
+ var next models.UID
if t.NextSibling != nil {
- next = int(t.NextSibling.Uid)
+ next = t.NextSibling.Uid
}
- child := -1
+ var child models.UID
if t.FirstChild != nil {
- child = int(t.FirstChild.Uid)
+ child = t.FirstChild.Uid
}
return fmt.Sprintf(
- "[%d] (parent:%v, next:%v, child:%v)",
+ "[%s] (parent:%s, next:%s, child:%s)",
t.Uid, parent, next, child,
)
}
@@ -141,9 +142,9 @@ type NewThreadWalkFn func(t *Thread, level int, currentErr error) error
// Implement interface to be able to sort threads by newest (max UID)
type ByUID []*Thread
-func getMaxUID(thread *Thread) uint32 {
+func getMaxUID(thread *Thread) models.UID {
// TODO: should we make this part of the Thread type to avoid recomputation?
- var Uid uint32
+ var Uid models.UID
_ = thread.Walk(func(t *Thread, _ int, currentErr error) error {
if t.Deleted || t.Hidden > 0 {
@@ -171,9 +172,9 @@ func (s ByUID) Less(i, j int) bool {
return maxUID_i < maxUID_j
}
-func SortThreadsBy(toSort []*Thread, sortBy []uint32) {
+func SortThreadsBy(toSort []*Thread, sortBy []models.UID) {
// build a map from sortBy
- uidMap := make(map[uint32]int)
+ uidMap := make(map[models.UID]int)
for i, uid := range sortBy {
uidMap[uid] = i
}
diff --git a/worker/types/thread_test.go b/worker/types/thread_test.go
index 669803d8..a6a0f327 100644
--- a/worker/types/thread_test.go
+++ b/worker/types/thread_test.go
@@ -4,16 +4,16 @@ import (
"fmt"
"strings"
"testing"
+
+ "git.sr.ht/~rjarry/aerc/models"
)
func genFakeTree() *Thread {
- tree := &Thread{
- Uid: 0,
- }
+ tree := new(Thread)
var prevChild *Thread
- for i := 1; i < 3; i++ {
+ for i := uint32(1); i < uint32(3); i++ {
child := &Thread{
- Uid: uint32(i * 10),
+ Uid: models.Uint32ToUid(i * 10),
Parent: tree,
PrevSibling: prevChild,
}
@@ -26,9 +26,9 @@ func genFakeTree() *Thread {
}
prevChild = child
var prevSecond *Thread
- for j := 1; j < 3; j++ {
+ for j := uint32(1); j < uint32(3); j++ {
second := &Thread{
- Uid: child.Uid + uint32(j),
+ Uid: models.Uint32ToUid(models.UidToUint32(child.Uid) + j),
Parent: child,
PrevSibling: prevSecond,
}
@@ -41,13 +41,13 @@ func genFakeTree() *Thread {
}
prevSecond = second
var prevThird *Thread
- limit := 3
+ limit := uint32(3)
if j == 2 {
limit = 8
}
- for k := 1; k < limit; k++ {
+ for k := uint32(1); k < limit; k++ {
third := &Thread{
- Uid: second.Uid*10 + uint32(k),
+ Uid: models.Uint32ToUid(models.UidToUint32(second.Uid)*10 + j),
Parent: second,
PrevSibling: prevThird,
}
@@ -107,7 +107,7 @@ func TestNewWalk(t *testing.T) {
func uidSeq(tree *Thread) string {
var seq []string
tree.Walk(func(t *Thread, _ int, _ error) error {
- seq = append(seq, fmt.Sprintf("%d", t.Uid))
+ seq = append(seq, string(t.Uid))
return nil
})
return strings.Join(seq, ".")
@@ -116,25 +116,25 @@ func uidSeq(tree *Thread) string {
func TestThread_AddChild(t *testing.T) {
tests := []struct {
name string
- seq []int
+ seq []models.UID
want string
}{
{
name: "ascending",
- seq: []int{1, 2, 3, 4, 5, 6},
- want: "0.1.2.3.4.5.6",
+ seq: []models.UID{"1", "2", "3", "4", "5", "6"},
+ want: ".1.2.3.4.5.6",
},
{
name: "descending",
- seq: []int{6, 5, 4, 3, 2, 1},
- want: "0.6.5.4.3.2.1",
+ seq: []models.UID{"6", "5", "4", "3", "2", "1"},
+ want: ".6.5.4.3.2.1",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- tree := &Thread{Uid: 0}
+ tree := new(Thread)
for _, i := range test.seq {
- tree.AddChild(&Thread{Uid: uint32(i)})
+ tree.AddChild(&Thread{Uid: i})
}
if got := uidSeq(tree); got != test.want {
t.Errorf("got: %s, but wanted: %s", got,
@@ -147,30 +147,30 @@ func TestThread_AddChild(t *testing.T) {
func TestThread_OrderedInsert(t *testing.T) {
tests := []struct {
name string
- seq []int
+ seq []models.UID
want string
}{
{
name: "ascending",
- seq: []int{1, 2, 3, 4, 5, 6},
- want: "0.1.2.3.4.5.6",
+ seq: []models.UID{"1", "2", "3", "4", "5", "6"},
+ want: ".1.2.3.4.5.6",
},
{
name: "descending",
- seq: []int{6, 5, 4, 3, 2, 1},
- want: "0.1.2.3.4.5.6",
+ seq: []models.UID{"6", "5", "4", "3", "2", "1"},
+ want: ".1.2.3.4.5.6",
},
{
name: "mixed",
- seq: []int{2, 1, 6, 3, 4, 5},
- want: "0.1.2.3.4.5.6",
+ seq: []models.UID{"2", "1", "6", "3", "4", "5"},
+ want: ".1.2.3.4.5.6",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- tree := &Thread{Uid: 0}
+ tree := new(Thread)
for _, i := range test.seq {
- tree.OrderedInsert(&Thread{Uid: uint32(i)})
+ tree.OrderedInsert(&Thread{Uid: i})
}
if got := uidSeq(tree); got != test.want {
t.Errorf("got: %s, but wanted: %s", got,
@@ -183,32 +183,32 @@ func TestThread_OrderedInsert(t *testing.T) {
func TestThread_InsertCmd(t *testing.T) {
tests := []struct {
name string
- seq []int
+ seq []models.UID
want string
}{
{
name: "ascending",
- seq: []int{1, 2, 3, 4, 5, 6},
- want: "0.6.4.2.1.3.5",
+ seq: []models.UID{"1", "2", "3", "4", "5", "6"},
+ want: ".6.4.2.1.3.5",
},
{
name: "descending",
- seq: []int{6, 5, 4, 3, 2, 1},
- want: "0.6.4.2.1.3.5",
+ seq: []models.UID{"6", "5", "4", "3", "2", "1"},
+ want: ".6.4.2.1.3.5",
},
{
name: "mixed",
- seq: []int{2, 1, 6, 3, 4, 5},
- want: "0.6.4.2.1.3.5",
+ seq: []models.UID{"2", "1", "6", "3", "4", "5"},
+ want: ".6.4.2.1.3.5",
},
}
- sortMap := map[uint32]int{
- uint32(6): 1,
- uint32(4): 2,
- uint32(2): 3,
- uint32(1): 4,
- uint32(3): 5,
- uint32(5): 6,
+ sortMap := map[models.UID]int{
+ "6": 1,
+ "4": 2,
+ "2": 3,
+ "1": 4,
+ "3": 5,
+ "5": 6,
}
// bigger compares the new child with the next node and returns true if
@@ -219,9 +219,9 @@ func TestThread_InsertCmd(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- tree := &Thread{Uid: 0}
+ tree := new(Thread)
for _, i := range test.seq {
- tree.InsertCmp(&Thread{Uid: uint32(i)}, bigger)
+ tree.InsertCmp(&Thread{Uid: i}, bigger)
}
if got := uidSeq(tree); got != test.want {
t.Errorf("got: %s, but wanted: %s", got,