aeus/pkg/cache/memory/cache.go

116 lines
2.1 KiB
Go

package memory
import (
"context"
"fmt"
"reflect"
"sync"
"time"
"errors"
)
var (
ErrWongType = errors.New("val must be a pointer")
ErrNotExists = errors.New("not exists")
ErrAaddressable = errors.New("cannot set value: val is not addressable")
)
type memCache struct {
opts Options
items map[string]Item
sync.RWMutex
}
func (c *memCache) Load(ctx context.Context, key string, val any) error {
c.RWMutex.RLock()
defer c.RWMutex.RUnlock()
item, found := c.items[key]
if !found {
return ErrNotExists
}
if item.Expired() {
return ErrNotExists
}
refValue := reflect.ValueOf(val)
if refValue.Type().Kind() != reflect.Ptr {
return ErrWongType
}
refElem := refValue.Elem()
if !refElem.CanSet() {
return ErrAaddressable
}
targetValue := reflect.Indirect(reflect.ValueOf(item.Value))
if targetValue.Type() != refElem.Type() {
return fmt.Errorf("type mismatch: expected %v, got %v", refElem.Type(), targetValue.Type())
}
refElem.Set(targetValue)
return nil
}
func (c *memCache) Exists(ctx context.Context, key string) (bool, error) {
c.RWMutex.RLock()
defer c.RWMutex.RUnlock()
item, found := c.items[key]
if !found {
return false, nil
}
if item.Expired() {
return false, nil
}
return true, nil
}
func (c *memCache) Store(ctx context.Context, key string, val any, d time.Duration) error {
var e int64
if d == DefaultExpiration {
d = c.opts.Expiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.RWMutex.Lock()
defer c.RWMutex.Unlock()
c.items[key] = Item{
Value: val,
Expiration: e,
}
return nil
}
func (c *memCache) Delete(ctx context.Context, key string) error {
c.RWMutex.Lock()
defer c.RWMutex.Unlock()
_, found := c.items[key]
if !found {
return ErrNotExists
}
delete(c.items, key)
return nil
}
func (m *memCache) String() string {
return "memory"
}
// NewCache returns a new cache.
func NewCache(opts ...Option) *memCache {
options := NewOptions(opts...)
items := make(map[string]Item)
if len(options.Items) > 0 {
items = options.Items
}
return &memCache{
opts: options,
items: items,
}
}