1
0
Fork 0
mirror of https://git.sr.ht/~rjarry/aerc synced 2025-10-27 13:15:09 +01:00
aerc/commands/msg/archive.go
Moritz Poldrack fc5b6896ff chore: switch to using stdlib helper functions
Go has evolved significantly over the years and has introduced some
handy helper functions that make the code easier to read.

Use helper functions like slices.Contains, map.Copy, and
strings.CutPrefix, when appropriate.

Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
2025-08-04 12:47:05 +02:00

177 lines
4 KiB
Go

package msg
import (
"fmt"
"slices"
"strings"
"sync"
"time"
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
const (
ARCHIVE_FLAT = "flat"
ARCHIVE_YEAR = "year"
ARCHIVE_MONTH = "month"
)
var ARCHIVE_TYPES = []string{ARCHIVE_FLAT, ARCHIVE_YEAR, ARCHIVE_MONTH}
type Archive struct {
MultiFileStrategy *types.MultiFileStrategy `opt:"-m" action:"ParseMFS" complete:"CompleteMFS" desc:"Multi-file strategy."`
Type string `opt:"type" action:"ParseArchiveType" metavar:"flat|year|month" complete:"CompleteType" desc:"Archiving scheme."`
}
func (a *Archive) ParseMFS(arg string) error {
if arg != "" {
mfs, ok := types.StrToStrategy[arg]
if !ok {
return fmt.Errorf("invalid multi-file strategy %s", arg)
}
a.MultiFileStrategy = &mfs
}
return nil
}
func (a *Archive) ParseArchiveType(arg string) error {
if slices.Contains(ARCHIVE_TYPES, arg) {
a.Type = arg
return nil
}
return fmt.Errorf("invalid archive type")
}
func init() {
commands.Register(Archive{})
}
func (Archive) Description() string {
return "Move the selected message to the archive."
}
func (Archive) Context() commands.CommandContext {
return commands.MESSAGE_LIST | commands.MESSAGE_VIEWER
}
func (Archive) Aliases() []string {
return []string{"archive"}
}
func (Archive) CompleteMFS(arg string) []string {
return commands.FilterList(types.StrategyStrs(), arg, nil)
}
func (*Archive) CompleteType(arg string) []string {
return commands.FilterList(ARCHIVE_TYPES, arg, nil)
}
func (a Archive) Execute(args []string) error {
h := newHelper()
msgs, err := h.messages()
if err != nil {
return err
}
err = archive(msgs, a.MultiFileStrategy, a.Type)
return err
}
func archive(msgs []*models.MessageInfo, mfs *types.MultiFileStrategy,
archiveType string,
) error {
h := newHelper()
acct, err := h.account()
if err != nil {
return err
}
store, err := h.store()
if err != nil {
return err
}
var uids []models.UID
for _, msg := range msgs {
uids = append(uids, msg.Uid)
}
archiveDir := acct.AccountConfig().Archive
marker := store.Marker()
marker.ClearVisualMark()
next := findNextNonDeleted(uids, store)
var uidMap map[string][]models.UID
switch archiveType {
case ARCHIVE_MONTH:
uidMap = groupBy(msgs, func(msg *models.MessageInfo) string {
dir := strings.Join([]string{
archiveDir,
fmt.Sprintf("%d", msg.Envelope.Date.Year()),
fmt.Sprintf("%02d", msg.Envelope.Date.Month()),
}, app.SelectedAccount().Worker().PathSeparator(),
)
return dir
})
case ARCHIVE_YEAR:
uidMap = groupBy(msgs, func(msg *models.MessageInfo) string {
dir := strings.Join([]string{
archiveDir,
fmt.Sprintf("%v", msg.Envelope.Date.Year()),
}, app.SelectedAccount().Worker().PathSeparator(),
)
return dir
})
case ARCHIVE_FLAT:
uidMap = make(map[string][]models.UID)
uidMap[archiveDir] = commands.UidsFromMessageInfos(msgs)
}
var wg sync.WaitGroup
wg.Add(len(uidMap))
success := true
for dir, uids := range uidMap {
store.Move(uids, dir, true, mfs, func(
msg types.WorkerMessage,
) {
switch msg := msg.(type) {
case *types.Done:
wg.Done()
case *types.Error:
app.PushError(msg.Error.Error())
success = false
wg.Done()
marker.Remark()
}
})
}
// we need to do that in the background, else we block the main thread
go func() {
defer log.PanicHandler()
wg.Wait()
if success {
var s string
if len(uids) > 1 {
s = "%d messages archived to %s"
} else {
s = "%d message archived to %s"
}
app.PushStatus(fmt.Sprintf(s, len(uids), archiveDir), 10*time.Second)
handleDone(acct, next, store)
}
}()
return nil
}
func groupBy(msgs []*models.MessageInfo,
grouper func(*models.MessageInfo) string,
) map[string][]models.UID {
m := make(map[string][]models.UID)
for _, msg := range msgs {
group := grouper(msg)
m[group] = append(m[group], msg.Uid)
}
return m
}