aboutsummaryrefslogtreecommitdiff
path: root/worker/maildir/search.go
blob: cf9547537b5e12496b2323b1590318bd2549aaf3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package maildir

import (
	"context"
	"runtime"
	"sync"

	"git.sr.ht/~rjarry/aerc/log"
	"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) {
	requiredParts := lib.GetRequiredParts(criteria)

	w.worker.Debugf("Required parts bitmask for search: %b", requiredParts)

	keys, err := w.c.UIDs(*w.selected)
	if err != nil {
		return nil, err
	}

	matchedUids := []uint32{}
	mu := sync.Mutex{}
	wg := sync.WaitGroup{}
	// Hard limit at 2x CPU cores
	max := runtime.NumCPU() * 2
	limit := make(chan struct{}, max)
	for _, key := range keys {
		select {
		case <-ctx.Done():
			return nil, context.Canceled
		default:
			limit <- struct{}{}
			wg.Add(1)
			go func(key uint32) {
				defer log.PanicHandler()
				defer wg.Done()
				success, err := w.searchKey(key, criteria, requiredParts)
				if err != nil {
					// don't return early so that we can still get some results
					w.worker.Errorf("Failed to search key %d: %v", key, err)
				} else if success {
					mu.Lock()
					matchedUids = append(matchedUids, key)
					mu.Unlock()
				}
				<-limit
			}(key)

		}
	}
	wg.Wait()
	return matchedUids, nil
}

// Execute the search criteria for the given key, returns true if search succeeded
func (w *Worker) searchKey(key uint32, criteria *types.SearchCriteria,
	parts lib.MsgParts,
) (bool, error) {
	message, err := w.c.Message(*w.selected, key)
	if err != nil {
		return false, err
	}
	return lib.SearchMessage(message, criteria, parts)
}