package ezcache import ( "sync" "time" ) type ezc[K comparable, V any] struct { fetch Fetcher[K, V] exp time.Duration cache map[K]V setTime map[K]time.Time lock sync.RWMutex timeLock sync.RWMutex } func New[K comparable, V any](fetcher Fetcher[K, V], exp time.Duration) (Cache[K, V], error) { c := &ezc[K, V]{} err := c.SetFetcher(fetcher) if err != nil { return nil, err } err = c.SetExpiry(exp) if err != nil { return nil, err } c.cache = make(map[K]V) c.setTime = make(map[K]time.Time) return c, nil } func (c *ezc[K, V]) Get(key K) (V, error) { c.timeLock.RLock() setTime, ok := c.setTime[key] c.timeLock.RUnlock() if ok && time.Since(setTime) <= c.exp { c.lock.RLock() val, ok := c.cache[key] c.lock.RUnlock() if ok { return val, nil } } val, err := c.fetch(key) if err != nil { return val, err } c.lock.Lock() defer c.lock.Unlock() c.cache[key] = val c.timeLock.Lock() defer c.timeLock.Unlock() c.setTime[key] = time.Now() return val, nil } func (c *ezc[K, V]) SetFetcher(f Fetcher[K, V]) error { if f == nil { return ErrInvalidFetcher } c.fetch = f return nil } func (c *ezc[K, V]) SetExpiry(exp time.Duration) error { if exp <= 0 { return ErrInvalidExpiry } c.exp = exp return nil }