aboutsummaryrefslogtreecommitdiffstats
path: root/worker/jmap/cache/cache.go
blob: 429dafe98cb76a1ca8f5657b28b9515fd4ed103c (plain) (blame)
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package cache

import (
	"errors"
	"os"
	"path"
	"strings"

	"git.sr.ht/~rjarry/aerc/lib/log"
	"git.sr.ht/~rjarry/aerc/lib/xdg"
	"github.com/syndtr/goleveldb/leveldb"
	"github.com/syndtr/goleveldb/leveldb/util"
)

type JMAPCache struct {
	mem      map[string][]byte
	file     *leveldb.DB
	blobsDir string
}

func NewJMAPCache(state, blobs bool, accountName string) *JMAPCache {
	c := new(JMAPCache)
	cacheDir := xdg.CachePath()
	if state && cacheDir != "" {
		var err error
		dir := path.Join(cacheDir, "aerc", accountName, "state")
		_ = os.MkdirAll(dir, 0o700)
		c.file, err = leveldb.OpenFile(dir, nil)
		if err != nil {
			log.Errorf("failed to open goleveldb: %s", err)
			c.mem = make(map[string][]byte)
		}
	} else {
		c.mem = make(map[string][]byte)
	}
	if blobs && cacheDir != "" {
		c.blobsDir = path.Join(cacheDir, "aerc", accountName, "blobs")
	}
	return c
}

var notfound = errors.New("key not found")

func (c *JMAPCache) get(key string) ([]byte, error) {
	switch {
	case c.file != nil:
		return c.file.Get([]byte(key), nil)
	case c.mem != nil:
		value, ok := c.mem[key]
		if !ok {
			return nil, notfound
		}
		return value, nil
	}
	panic("jmap cache with no backend")
}

func (c *JMAPCache) put(key string, value []byte) error {
	switch {
	case c.file != nil:
		return c.file.Put([]byte(key), value, nil)
	case c.mem != nil:
		c.mem[key] = value
		return nil
	}
	panic("jmap cache with no backend")
}

func (c *JMAPCache) delete(key string) error {
	switch {
	case c.file != nil:
		return c.file.Delete([]byte(key), nil)
	case c.mem != nil:
		delete(c.mem, key)
		return nil
	}
	panic("jmap cache with no backend")
}

func (c *JMAPCache) purge(prefix string) error {
	switch {
	case c.file != nil:
		txn, err := c.file.OpenTransaction()
		if err != nil {
			return err
		}
		iter := txn.NewIterator(util.BytesPrefix([]byte(prefix)), nil)
		for iter.Next() {
			err = txn.Delete(iter.Key(), nil)
			if err != nil {
				break
			}
		}
		iter.Release()
		if err != nil {
			txn.Discard()
			return err
		}
		return txn.Commit()
	case c.mem != nil:
		for key := range c.mem {
			if strings.HasPrefix(key, prefix) {
				delete(c.mem, key)
			}
		}
		return nil
	}
	panic("jmap cache with no backend")
}