probo/pkg/store/store.go

171 lines
2.6 KiB
Go

package store
import (
"fmt"
"sync"
"time"
"github.com/google/uuid"
)
type Storable interface {
GetHash() string
GetID() string
SetID(string)
SetCreatedAt(t time.Time)
SetUpdatedAt(t time.Time)
GetCreatedAt() time.Time
GetUpdatedAt() time.Time
}
type Storer[T Storable] interface {
Create(T) (T, error)
ReadAll() []T
Read(string) (T, error)
Update(T, string) (T, error)
Delete(string) (T, error)
}
type FilterStorer[T Storable] interface {
Storer[T]
Filter([]T, func(T) bool) []T
}
type Store[T Storable] struct {
ids map[string]T
hashes map[string]T
lock sync.RWMutex
}
type FilterStore[T Storable] struct {
*Store[T]
}
func NewFilterStore[T Storable]() *FilterStore[T] {
return &FilterStore[T]{NewStore[T]()}
}
func (fs *FilterStore[T]) Filter(slice []T, f func(T) bool) []T {
result := make([]T, 0)
for _, item := range slice {
if f(item) {
result = append(result, item)
}
}
return result
}
func NewStore[T Storable]() *Store[T] {
store := new(Store[T])
store.ids = make(map[string]T)
store.hashes = make(map[string]T)
return store
}
func (s *Store[T]) Create(entity T) (T, error) {
s.lock.Lock()
defer s.lock.Unlock()
if hash := entity.GetHash(); hash != "" {
storedEntity, ok := s.hashes[hash]
if ok {
return storedEntity, nil
}
s.hashes[hash] = entity
}
id := entity.GetID()
if id == "" {
id = uuid.New().String()
}
entity.SetID(id)
if !entity.GetCreatedAt().IsZero() {
entity.SetUpdatedAt(time.Now())
} else {
entity.SetCreatedAt(time.Now())
}
if entity.GetUpdatedAt().IsZero() {
entity.SetUpdatedAt(time.Now())
}
s.ids[id] = entity
return entity, nil
}
func (s *Store[T]) ReadAll() []T {
s.lock.Lock()
defer s.lock.Unlock()
result := make([]T, 0)
for _, v := range s.ids {
result = append(result, v)
}
return result
}
func (s *Store[T]) Read(id string) (T, error) {
s.lock.RLock()
defer s.lock.RUnlock()
entity, ok := s.ids[id]
if !ok {
return entity, fmt.Errorf("Entity with ID %s was not found in the store.", id)
}
return entity, nil
}
func (s *Store[T]) Update(entity T, id string) (T, error) {
sEntity, err := s.Read(id)
if err != nil {
return sEntity, err
}
s.lock.Lock()
defer s.lock.Unlock()
entity.SetID(id)
s.ids[id] = entity
if hash := entity.GetHash(); hash != "" {
s.hashes[hash] = entity
}
entity.SetUpdatedAt(time.Now())
return entity, nil
}
func (s *Store[T]) Delete(id string) (T, error) {
sEntity, err := s.Read(id)
if err != nil {
return sEntity, err
}
s.lock.Lock()
defer s.lock.Unlock()
delete(s.ids, id)
if hash := sEntity.GetHash(); hash != "" {
delete(s.hashes, hash)
}
return sEntity, nil
}