data/Collection.go
2023-07-19 11:52:13 +02:00

176 lines
3.2 KiB
Go

package data
import (
"os"
"path/filepath"
"reflect"
"sync"
)
// Collection is the interface to access and modify your data.
type Collection[T any] interface {
All() <-chan *T
Clear()
Delete(key string)
Exists(key string) bool
Filter(func(*T) bool) <-chan *T
Get(key string) (value *T, err error)
Set(key string, value *T)
Sync()
}
// collection is a hash map of homogeneous data.
type collection[T any] struct {
data sync.Map
storage Storage[T]
name string
root string
}
// New creates a new collection with the given name.
func New[T any](namespace string, storage Storage[T]) (Collection[T], error) {
name := reflect.TypeOf((*T)(nil)).Elem().Name()
c := &collection[T]{
name: name,
}
if storage == nil {
return c, nil
}
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}
c.storage = storage
c.root = filepath.Join(home, ".data", namespace)
err = os.MkdirAll(c.root, 0700)
if err != nil {
return nil, err
}
return c, storage.Init(c)
}
// NewFile creates a new collection with the given name and the File storage system.
func NewFile[T any](namespace string) (Collection[T], error) {
return New[T](namespace, &File[T]{})
}
// All returns a channel of all objects in the collection.
func (c *collection[T]) All() <-chan *T {
channel := make(chan *T)
go func() {
c.data.Range(func(key, value any) bool {
channel <- value.(*T)
return true
})
close(channel)
}()
return channel
}
// Clear deletes all objects from the collection.
func (c *collection[T]) Clear() {
c.data.Range(func(key, value any) bool {
c.Delete(key.(string))
return true
})
}
// Data returns the internal data structure.
func (c *collection[T]) Data() *sync.Map {
return &c.data
}
// Delete deletes a key from the collection.
func (c *collection[T]) Delete(key string) {
c.data.Delete(key)
if c.storage == nil {
return
}
err := c.storage.Delete(key)
if err != nil {
panic(err)
}
}
// Exists returns whether or not the key exists.
func (c *collection[T]) Exists(key string) bool {
_, exists := c.data.Load(key)
return exists
}
// Get returns the value for the given key.
func (c *collection[T]) Get(key string) (*T, error) {
value, exists := c.data.Load(key)
if !exists {
return nil, &KeyNotFoundError{Key: key}
}
return value.(*T), nil
}
// Filter returns a channel of all objects that pass the given filter function.
func (c *collection[T]) Filter(filter func(*T) bool) <-chan *T {
channel := make(chan *T)
go func() {
c.data.Range(func(key, value any) bool {
if filter(value.(*T)) {
channel <- value.(*T)
}
return true
})
close(channel)
}()
return channel
}
// Name returns the name of the collection.
func (c *collection[T]) Name() string {
return c.name
}
// Root returns the root path.
func (c *collection[T]) Root() string {
return c.root
}
// Set sets the value for the given key.
func (c *collection[T]) Set(key string, value *T) {
c.data.Store(key, value)
if c.storage == nil {
return
}
err := c.storage.Set(key, value)
if err != nil {
panic(err)
}
}
// Sync waits for all disk writes to finish before it returns.
func (c *collection[T]) Sync() {
if c.storage == nil {
return
}
c.storage.Sync()
}