changed store.Store to accept generic key type

This commit is contained in:
Gani Georgiev
2024-12-23 15:44:00 +02:00
parent e18116d859
commit 39df26ee21
12 changed files with 57 additions and 54 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/spf13/cast"
)
var cachedPatterns = store.New[*regexp.Regexp](nil)
var cachedPatterns = store.New[string, *regexp.Regexp](nil)
// SubtractSlice returns a new slice with only the "base" elements
// that don't exist in "subtract".
+1 -1
View File
@@ -34,7 +34,7 @@ type Event struct {
hook.Event
data store.Store[any]
data store.Store[string, any]
}
// RWUnwrapper specifies that an http.ResponseWriter could be "unwrapped"
+27 -27
View File
@@ -9,15 +9,15 @@ import (
const ShrinkThreshold = 200 // the number is arbitrary chosen
// Store defines a concurrent safe in memory key-value data store.
type Store[T any] struct {
data map[string]T
type Store[K comparable, T any] struct {
data map[K]T
mu sync.RWMutex
deleted int64
}
// New creates a new Store[T] instance with a shallow copy of the provided data (if any).
func New[T any](data map[string]T) *Store[T] {
s := &Store[T]{}
func New[K comparable, T any](data map[K]T) *Store[K, T] {
s := &Store[K, T]{}
s.Reset(data)
@@ -26,24 +26,24 @@ func New[T any](data map[string]T) *Store[T] {
// Reset clears the store and replaces the store data with a
// shallow copy of the provided newData.
func (s *Store[T]) Reset(newData map[string]T) {
func (s *Store[K, T]) Reset(newData map[K]T) {
s.mu.Lock()
defer s.mu.Unlock()
if len(newData) > 0 {
s.data = make(map[string]T, len(newData))
s.data = make(map[K]T, len(newData))
for k, v := range newData {
s.data[k] = v
}
} else {
s.data = make(map[string]T)
s.data = make(map[K]T)
}
s.deleted = 0
}
// Length returns the current number of elements in the store.
func (s *Store[T]) Length() int {
func (s *Store[K, T]) Length() int {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -51,14 +51,14 @@ func (s *Store[T]) Length() int {
}
// RemoveAll removes all the existing store entries.
func (s *Store[T]) RemoveAll() {
func (s *Store[K, T]) RemoveAll() {
s.Reset(nil)
}
// Remove removes a single entry from the store.
//
// Remove does nothing if key doesn't exist in the store.
func (s *Store[T]) Remove(key string) {
func (s *Store[K, T]) Remove(key K) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -69,7 +69,7 @@ func (s *Store[T]) Remove(key string) {
//
// @todo remove after https://github.com/golang/go/issues/20135
if s.deleted >= ShrinkThreshold {
newData := make(map[string]T, len(s.data))
newData := make(map[K]T, len(s.data))
for k, v := range s.data {
newData[k] = v
}
@@ -79,7 +79,7 @@ func (s *Store[T]) Remove(key string) {
}
// Has checks if element with the specified key exist or not.
func (s *Store[T]) Has(key string) bool {
func (s *Store[K, T]) Has(key K) bool {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -91,7 +91,7 @@ func (s *Store[T]) Has(key string) bool {
// Get returns a single element value from the store.
//
// If key is not set, the zero T value is returned.
func (s *Store[T]) Get(key string) T {
func (s *Store[K, T]) Get(key K) T {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -99,7 +99,7 @@ func (s *Store[T]) Get(key string) T {
}
// GetOk is similar to Get but returns also a boolean indicating whether the key exists or not.
func (s *Store[T]) GetOk(key string) (T, bool) {
func (s *Store[K, T]) GetOk(key K) (T, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -109,11 +109,11 @@ func (s *Store[T]) GetOk(key string) (T, bool) {
}
// GetAll returns a shallow copy of the current store data.
func (s *Store[T]) GetAll() map[string]T {
func (s *Store[K, T]) GetAll() map[K]T {
s.mu.RLock()
defer s.mu.RUnlock()
var clone = make(map[string]T, len(s.data))
var clone = make(map[K]T, len(s.data))
for k, v := range s.data {
clone[k] = v
@@ -123,7 +123,7 @@ func (s *Store[T]) GetAll() map[string]T {
}
// Values returns a slice with all of the current store values.
func (s *Store[T]) Values() []T {
func (s *Store[K, T]) Values() []T {
s.mu.RLock()
defer s.mu.RUnlock()
@@ -137,12 +137,12 @@ func (s *Store[T]) Values() []T {
}
// Set sets (or overwrite if already exist) a new value for key.
func (s *Store[T]) Set(key string, value T) {
func (s *Store[K, T]) Set(key K, value T) {
s.mu.Lock()
defer s.mu.Unlock()
if s.data == nil {
s.data = make(map[string]T)
s.data = make(map[K]T)
}
s.data[key] = value
@@ -150,7 +150,7 @@ func (s *Store[T]) Set(key string, value T) {
// GetOrSet retrieves a single existing value for the provided key
// or stores a new one if it doesn't exist.
func (s *Store[T]) GetOrSet(key string, setFunc func() T) T {
func (s *Store[K, T]) GetOrSet(key K, setFunc func() T) T {
// lock only reads to minimize locks contention
s.mu.RLock()
v, ok := s.data[key]
@@ -160,7 +160,7 @@ func (s *Store[T]) GetOrSet(key string, setFunc func() T) T {
s.mu.Lock()
v = setFunc()
if s.data == nil {
s.data = make(map[string]T)
s.data = make(map[K]T)
}
s.data[key] = v
s.mu.Unlock()
@@ -174,12 +174,12 @@ func (s *Store[T]) GetOrSet(key string, setFunc func() T) T {
// This method is similar to Set() but **it will skip adding new elements**
// to the store if the store length has reached the specified limit.
// false is returned if maxAllowedElements limit is reached.
func (s *Store[T]) SetIfLessThanLimit(key string, value T, maxAllowedElements int) bool {
func (s *Store[K, T]) SetIfLessThanLimit(key K, value T, maxAllowedElements int) bool {
s.mu.Lock()
defer s.mu.Unlock()
if s.data == nil {
s.data = make(map[string]T)
s.data = make(map[K]T)
}
// check for existing item
@@ -200,8 +200,8 @@ func (s *Store[T]) SetIfLessThanLimit(key string, value T, maxAllowedElements in
// provided JSON data into the store.
//
// The store entries that match with the ones from the data will be overwritten with the new value.
func (s *Store[T]) UnmarshalJSON(data []byte) error {
raw := map[string]T{}
func (s *Store[K, T]) UnmarshalJSON(data []byte) error {
raw := map[K]T{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
@@ -210,7 +210,7 @@ func (s *Store[T]) UnmarshalJSON(data []byte) error {
defer s.mu.Unlock()
if s.data == nil {
s.data = make(map[string]T)
s.data = make(map[K]T)
}
for k, v := range raw {
@@ -222,6 +222,6 @@ func (s *Store[T]) UnmarshalJSON(data []byte) error {
// MarshalJSON implements [json.Marshaler] and export the current
// store data into valid JSON.
func (s *Store[T]) MarshalJSON() ([]byte, error) {
func (s *Store[K, T]) MarshalJSON() ([]byte, error) {
return json.Marshal(s.GetAll())
}
+5 -5
View File
@@ -227,7 +227,7 @@ func TestValues(t *testing.T) {
}
func TestSet(t *testing.T) {
s := store.Store[int]{}
s := store.Store[string, int]{}
data := map[string]int{"test1": 0, "test2": 1, "test3": 3}
@@ -281,7 +281,7 @@ func TestGetOrSet(t *testing.T) {
}
func TestSetIfLessThanLimit(t *testing.T) {
s := store.Store[int]{}
s := store.Store[string, int]{}
limit := 2
@@ -316,7 +316,7 @@ func TestSetIfLessThanLimit(t *testing.T) {
}
func TestUnmarshalJSON(t *testing.T) {
s := store.Store[string]{}
s := store.Store[string, string]{}
s.Set("b", "old") // should be overwritten
s.Set("c", "test3") // ensures that the old values are not removed
@@ -339,7 +339,7 @@ func TestUnmarshalJSON(t *testing.T) {
}
func TestMarshalJSON(t *testing.T) {
s := &store.Store[string]{}
s := &store.Store[string, string]{}
s.Set("a", "test1")
s.Set("b", "test2")
@@ -356,7 +356,7 @@ func TestMarshalJSON(t *testing.T) {
}
func TestShrink(t *testing.T) {
s := &store.Store[int]{}
s := &store.Store[string, int]{}
total := 1000
+2 -2
View File
@@ -9,13 +9,13 @@ import (
// Broker defines a struct for managing subscriptions clients.
type Broker struct {
store *store.Store[Client]
store *store.Store[string, Client]
}
// NewBroker initializes and returns a new Broker instance.
func NewBroker() *Broker {
return &Broker{
store: store.New[Client](nil),
store: store.New[string, Client](nil),
}
}
+2 -2
View File
@@ -37,7 +37,7 @@ import (
// Use the Registry.Load* methods to load templates into the registry.
func NewRegistry() *Registry {
return &Registry{
cache: store.New[*Renderer](nil),
cache: store.New[string, *Renderer](nil),
funcs: template.FuncMap{
"raw": func(str string) template.HTML {
return template.HTML(str)
@@ -50,7 +50,7 @@ func NewRegistry() *Registry {
//
// Use the Registry.Load* methods to load templates into the registry.
type Registry struct {
cache *store.Store[*Renderer]
cache *store.Store[string, *Renderer]
funcs template.FuncMap
}