diff --git a/examples/example.go b/examples/example.go index 97c9c71..f28ddaf 100644 --- a/examples/example.go +++ b/examples/example.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "time" @@ -14,27 +15,35 @@ func demo() { cache := mcache.NewCache[string]() - cache.Set("save indefinitely", "value without expiration", 0) // set value without expiration + fmt.Println("Setting key \"save indefinitely\" with value \"value without expiration\" and ttl 0") + cache.Set("save indefinitely", "value without expiration", 0) // set value without expiration + + fmt.Println("Setting key \"save for 1 second\" with value \"value will expire in 1 second\" and ttl 1 second") cache.Set("save for 1 second", "value will expire in 1 second", 1*time.Second) // set value with expiration in 1 second + fmt.Printf("\nRetrieving \"no such key\":\n") exists, err := cache.Has("no such key") // either exists or error can be checked if err != nil { // possible errors: // mcache.ErrKeyNotFound // mcache.ErrExpired - fmt.Println(err) - } - if !exists { - fmt.Println("key doen't exist or expired") + fmt.Printf("\tError retrieving \"no such key\": %v\n", err) + fmt.Printf("\tError is \"mcache.ErrKeyNotFound\": %t\n", errors.Is(err, mcache.ErrKeyNotFound)) } + fmt.Printf("\t\"no such key\" exists: %t\n", exists) - v, _ := cache.Get("save indefinitely") - fmt.Println(v) + fmt.Printf("\nRetrieving \"save indefinitely\":\n") + v, err := cache.Get("save indefinitely") + fmt.Printf("\t\"save indefinitely\" = %v\n", v) + fmt.Printf("\tError retrieving \"save indefinitely\": %v\n", err) time.Sleep(1 * time.Second) - v, _ = cache.Get("save for 1 second") - fmt.Println(v) // because key expired + fmt.Printf("\nRetrieving \"save for 1 second\" after 1+ second pause:\n") + v, err = cache.Get("save for 1 second") + fmt.Printf("\t\"save for 1 second\" value = %v\n", v) + fmt.Printf("\tError retrieving \"save for 1 second\": %v\n", err) + fmt.Printf("\tError is \"mcache.ErrExpired\": %t\n", errors.Is(err, mcache.ErrExpired)) } diff --git a/examples/readme_example/main.go b/examples/readme_example/main.go index 460edd1..c381082 100644 --- a/examples/readme_example/main.go +++ b/examples/readme_example/main.go @@ -16,7 +16,7 @@ func main() { v, err := cache.Get("key") if err != nil { // either error can be checked - fmt.Println(err) + fmt.Printf("Error: %v\n", err) } if v != "" { // or value can be checked for "empty" type value @@ -29,7 +29,7 @@ func main() { // possible errors: // mcache.ErrKeyNotFound - key doesn't exist // mcache.ErrExpired - key expired - fmt.Println(err) + fmt.Printf("Error: %v\n", err) } if exists { fmt.Println("key exists") diff --git a/main.go b/main.go index b935b55..1cf21dd 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,16 @@ package mcache import ( - "fmt" + "errors" "sync" "time" ) // Errors for cache -const ( - ErrKeyNotFound = "key not found" - ErrKeyExists = "key already exists" - ErrExpired = "key expired" +var ( + ErrKeyNotFound = errors.New("key not found") + ErrKeyExists = errors.New("key already exists") + ErrExpired = errors.New("key expired") ) // CacheItem is a struct for cache item @@ -48,20 +48,18 @@ func NewCache[T any](options ...func(*Cache[T])) *Cache[T] { return c } -// Set is a method for setting key-value pair -// If key already exists, and it's not expired, return error -// If key already exists, but it's expired, set new value and return nil -// If key doesn't exist, set new value and return nil +// Set is a method for setting key-value pair. +// If key already exists, and it's not expired, return error. +// If key already exists, but it's expired, set new value and return nil. +// If key doesn't exist, set new value and return nil. // If ttl is 0, set value without expiration func (c *Cache[T]) Set(key string, value T, ttl time.Duration) error { - var zeroTime time.Time - c.mx.RLock() cached, ok := c.data[key] c.mx.RUnlock() if ok { - if cached.expiration == zeroTime || cached.expiration.After(time.Now().Add(ttl)) { - return fmt.Errorf(ErrKeyExists) + if cached.expiration.IsZero() || cached.expiration.After(time.Now().Add(ttl)) { + return ErrKeyExists } } @@ -80,9 +78,9 @@ func (c *Cache[T]) Set(key string, value T, ttl time.Duration) error { return nil } -// Get is a method for getting value by key -// If key doesn't exist, return error -// If key exists, but it's expired, delete key, return zeroa value and error +// Get is a method for getting value by key. +// If key doesn't exist, return error. +// If key exists, but it's expired, delete key, return zero value and error. // If key exists and it's not expired, return value func (c *Cache[T]) Get(key string) (T, error) { var none T @@ -99,30 +97,29 @@ func (c *Cache[T]) Get(key string) (T, error) { return c.data[key].value, nil } -// Has is a method for checking if key exists. +// Has checks if key exists and if it's expired. // If key doesn't exist, return false. // If key exists, but it's expired, return false and delete key. -// If key exists and it's not expired, return true. +// If key exists and it's not expired, return true func (c *Cache[T]) Has(key string) (bool, error) { c.mx.RLock() d, ok := c.data[key] c.mx.RUnlock() if !ok { - return false, fmt.Errorf(ErrKeyNotFound) + return false, ErrKeyNotFound } - var zeroTime time.Time - if d.expiration != zeroTime && d.expiration.Before(time.Now()) { + if !d.expiration.IsZero() && d.expiration.Before(time.Now()) { c.mx.Lock() delete(c.data, key) c.mx.Unlock() - return false, fmt.Errorf(ErrExpired) + return false, ErrExpired } return true, nil } -// Del is a method for deleting key-value pair +// Del deletes a key-value pair func (c *Cache[T]) Del(key string) error { _, err := c.Has(key) if err != nil { @@ -135,7 +132,7 @@ func (c *Cache[T]) Del(key string) error { return nil } -// Clear is a method for clearing cache +// Clears cache by replacing it with a clean one func (c *Cache[T]) Clear() error { c.mx.Lock() c.data = make(map[string]CacheItem[T]) @@ -143,12 +140,12 @@ func (c *Cache[T]) Clear() error { return nil } -// Cleanup is a method for deleting expired keys +// Cleanup deletes expired keys from cache func (c *Cache[T]) Cleanup() { + now := time.Now() c.mx.Lock() - var zeroTime time.Time for k, v := range c.data { - if v.expiration != zeroTime && v.expiration.Before(time.Now()) { + if !v.expiration.IsZero() && v.expiration.Before(now) { delete(c.data, k) } } diff --git a/main_test.go b/main_test.go index 4638092..d20688b 100644 --- a/main_test.go +++ b/main_test.go @@ -15,8 +15,7 @@ type testItem struct { } func Test_SimpleTest_Mcache(t *testing.T) { - var c Cacher[string] // linter:ignore S1021 - c = NewCache[string]() + var c Cacher[string] = NewCache[string]() assert.NotNil(t, c) assert.IsType(t, &Cache[string]{}, c) @@ -46,7 +45,7 @@ func Test_SimpleTest_Mcache(t *testing.T) { _, err := c.Get(noSuchKey) assert.Error(t, err) - assert.Equal(t, ErrKeyNotFound, err.Error()) + assert.ErrorIs(t, ErrKeyNotFound, err) for _, item := range testItems { has, err := c.Has(item.key) @@ -58,7 +57,7 @@ func Test_SimpleTest_Mcache(t *testing.T) { has, err := c.Has(testItems[1].key) assert.Error(t, err) - assert.Equal(t, ErrExpired, err.Error()) + assert.ErrorIs(t, ErrExpired, err) assert.False(t, has) testItems = append(testItems[2:], testItems[0]) @@ -71,7 +70,7 @@ func Test_SimpleTest_Mcache(t *testing.T) { has, err := c.Has(item.key) assert.False(t, has) assert.Error(t, err) - assert.Equal(t, ErrKeyNotFound, err.Error()) + assert.ErrorIs(t, ErrKeyNotFound, err) } c.Set("key", "value", time.Second*1) @@ -86,7 +85,7 @@ func Test_SimpleTest_Mcache(t *testing.T) { err = c.Set("key", "not a newer value", 1) if err != nil { - assert.Equal(t, ErrKeyExists, err.Error()) + assert.ErrorIs(t, ErrKeyExists, err) } time.Sleep(time.Second * 2) err = c.Set("key", "even newer value", time.Second*1)