package cache
import (
"errors"
"os"
"path"
"strings"
"git.sr.ht/~rjarry/aerc/lib/xdg"
"git.sr.ht/~rjarry/aerc/log"
"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")
}