From 0c6d8c7fa966ab548a1f25c1c85d45e22eb44770 Mon Sep 17 00:00:00 2001 From: siddharth ravikumar Date: Mon, 6 Jun 2022 19:03:08 -0400 Subject: add `cache` package A simple key-value store. --- Makefile | 2 +- cache/cache.go | 54 ++++++++++++++++++++++++++++++++++++++++ cache/cache_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ version/version.go | 2 +- 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 cache/cache.go create mode 100644 cache/cache_test.go diff --git a/Makefile b/Makefile index f82cc30..d1b3232 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Copyright © 2022 siddharth ravikumar MOD=ricketyspace.net/peach -PKGS=${MOD}/client ${MOD}/nws ${MOD}/photon +PKGS=${MOD}/cache ${MOD}/client ${MOD}/nws ${MOD}/photon CSS=static/peach.min.css peach: fix fmt ${CSS} diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..fbc763e --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,54 @@ +// Copyright © 2022 siddharth ravikumar +// SPDX-License-Identifier: ISC + +// A simple in-memory cache store. +package cache + +import "time" + +// An item in the key-value cache store. +type item struct { + value string + expires time.Time // Time when the key-value expires +} + +// A key-value cache store. +type Cache struct { + store map[string]item +} + +// Returns a new empty cache store. +func NewCache() *Cache { + c := new(Cache) + c.store = make(map[string]item) + return c +} + +// Set the (key,value) item to the cache store. This item will be +// considered expired after time `expires`. +// +// Cache.Get will return an empty string once `expires` is past the +// current time. +func (c *Cache) Set(key, value string, expires time.Time) { + c.store[key] = item{ + value: value, + expires: expires, + } +} + +// Get an (key,value) item from the cache store by key. +// +// An empty string will be returned when if the key does not exist or +// if the item corresponding to the key has expired. An expired +// (key,value) item will be removed from the cache store. +func (c *Cache) Get(key string) string { + if _, ok := c.store[key]; !ok { + return "" + } + // Check if the item expired. + if time.Until(c.store[key].expires).Seconds() < 0 { + delete(c.store, key) + return "" + } + return c.store[key].value +} diff --git a/cache/cache_test.go b/cache/cache_test.go new file mode 100644 index 0000000..41e0d25 --- /dev/null +++ b/cache/cache_test.go @@ -0,0 +1,72 @@ +// Copyright © 2022 siddharth ravikumar +// SPDX-License-Identifier: ISC + +package cache + +import ( + "testing" + "time" +) + +func TestNewCache(t *testing.T) { + c := NewCache() + if c == nil { + t.Errorf("cache is nil") + return + } + if c.store == nil { + t.Errorf("cache.store is nil") + return + } + // Try manually adding an item. + c.store["foo"] = item{ + value: "bar", + expires: time.Now().Add(time.Second * 10), + } + if c.store["foo"].value != "bar" { + t.Errorf("cache.store['foo'] is not bar") + return + } +} + +func TestCacheSet(t *testing.T) { + c := NewCache() + if c == nil { + t.Errorf("cache is nil") + return + } + exp := time.Now().Add(time.Second * 10) + c.Set("foo", "bar", exp) + if c.store["foo"].value != "bar" { + t.Errorf("cache.store['foo'] is not bar") + return + } + if c.store["foo"].expires.Unix() != exp.Unix() { + t.Errorf("cache.store['foo'].expires no set correctly") + return + } +} + +func TestCacheGet(t *testing.T) { + c := NewCache() + if c == nil { + t.Errorf("cache is nil") + return + } + + // Test 1 + exp := time.Now().Add(time.Second * 10) + c.Set("foo", "bar", exp) + if c.Get("foo") != "bar" { + t.Errorf("cache.Get(foo) is not bar") + return + } + + // Test 2 + exp = time.Now().Add(time.Second * -10) + c.Set("sna", "fu", exp) + if c.Get("sna") != "" { + t.Errorf("cache.Get(sna) is not empty: %s", c.Get("sna")) + return + } +} diff --git a/version/version.go b/version/version.go index 36def98..ca288f9 100644 --- a/version/version.go +++ b/version/version.go @@ -4,4 +4,4 @@ // Peach version. package version -const Version = "0.3.1.dev1" +const Version = "0.3.1.dev2" -- cgit v1.2.3