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, } }