2021-02-12 16:33:16 +03:00
|
|
|
package store
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/patrickmn/go-cache"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewStore returns a memory store
|
|
|
|
func NewStore(opts ...Option) Store {
|
|
|
|
return &memoryStore{
|
|
|
|
opts: NewOptions(opts...),
|
|
|
|
store: cache.New(cache.NoExpiration, 5*time.Minute),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Connect(ctx context.Context) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Disconnect(ctx context.Context) error {
|
|
|
|
m.store.Flush()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type memoryStore struct {
|
|
|
|
opts Options
|
|
|
|
store *cache.Cache
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) key(prefix, key string) string {
|
|
|
|
return filepath.Join(prefix, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) prefix(database, table string) string {
|
|
|
|
if len(database) == 0 {
|
|
|
|
database = m.opts.Database
|
|
|
|
}
|
|
|
|
if len(table) == 0 {
|
|
|
|
table = m.opts.Table
|
|
|
|
}
|
|
|
|
return filepath.Join(database, table)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) exists(prefix, key string) error {
|
|
|
|
key = m.key(prefix, key)
|
|
|
|
|
|
|
|
_, found := m.store.Get(key)
|
|
|
|
if !found {
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) get(prefix, key string, val interface{}) error {
|
|
|
|
key = m.key(prefix, key)
|
|
|
|
|
|
|
|
r, found := m.store.Get(key)
|
|
|
|
if !found {
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, ok := r.([]byte)
|
|
|
|
if !ok {
|
|
|
|
return ErrNotFound
|
|
|
|
}
|
|
|
|
|
2021-02-14 16:16:01 +03:00
|
|
|
return m.opts.Codec.Unmarshal(buf, val)
|
2021-02-12 16:33:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) delete(prefix, key string) {
|
|
|
|
key = m.key(prefix, key)
|
|
|
|
m.store.Delete(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) list(prefix string, limit, offset uint) []string {
|
|
|
|
allItems := m.store.Items()
|
|
|
|
allKeys := make([]string, len(allItems))
|
|
|
|
i := 0
|
|
|
|
|
|
|
|
for k := range allItems {
|
|
|
|
if !strings.HasPrefix(k, prefix+"/") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
allKeys[i] = strings.TrimPrefix(k, prefix+"/")
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
if limit != 0 || offset != 0 {
|
|
|
|
sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] })
|
|
|
|
sort.Slice(allKeys, func(i, j int) bool { return allKeys[i] < allKeys[j] })
|
|
|
|
end := len(allKeys)
|
|
|
|
if limit > 0 {
|
|
|
|
calcLimit := int(offset + limit)
|
|
|
|
if calcLimit < end {
|
|
|
|
end = calcLimit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if int(offset) >= end {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return allKeys[offset:end]
|
|
|
|
}
|
|
|
|
|
|
|
|
return allKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Init(opts ...Option) error {
|
|
|
|
for _, o := range opts {
|
|
|
|
o(&m.opts)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) String() string {
|
|
|
|
return "memory"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Name() string {
|
|
|
|
return m.opts.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
|
|
|
|
prefix := m.prefix(m.opts.Database, m.opts.Table)
|
|
|
|
return m.exists(prefix, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error {
|
|
|
|
readOpts := NewReadOptions(opts...)
|
|
|
|
prefix := m.prefix(readOpts.Database, readOpts.Table)
|
|
|
|
return m.get(prefix, key, val)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {
|
|
|
|
writeOpts := NewWriteOptions(opts...)
|
|
|
|
|
|
|
|
prefix := m.prefix(writeOpts.Database, writeOpts.Table)
|
|
|
|
|
|
|
|
key = m.key(prefix, key)
|
|
|
|
|
|
|
|
buf, err := m.opts.Codec.Marshal(val)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.store.Set(key, buf, writeOpts.TTL)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
|
|
|
|
deleteOptions := NewDeleteOptions(opts...)
|
|
|
|
|
|
|
|
prefix := m.prefix(deleteOptions.Database, deleteOptions.Table)
|
|
|
|
m.delete(prefix, key)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) Options() Options {
|
|
|
|
return m.opts
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *memoryStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
|
|
|
|
listOptions := NewListOptions(opts...)
|
|
|
|
|
|
|
|
prefix := m.prefix(listOptions.Database, listOptions.Table)
|
|
|
|
keys := m.list(prefix, listOptions.Limit, listOptions.Offset)
|
|
|
|
|
|
|
|
if len(listOptions.Prefix) > 0 {
|
|
|
|
var prefixKeys []string
|
|
|
|
for _, k := range keys {
|
|
|
|
if strings.HasPrefix(k, listOptions.Prefix) {
|
|
|
|
prefixKeys = append(prefixKeys, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
keys = prefixKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(listOptions.Suffix) > 0 {
|
|
|
|
var suffixKeys []string
|
|
|
|
for _, k := range keys {
|
|
|
|
if strings.HasSuffix(k, listOptions.Suffix) {
|
|
|
|
suffixKeys = append(suffixKeys, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
keys = suffixKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys, nil
|
|
|
|
}
|