2023-07-04 22:20:26 +00:00
|
|
|
package ocean
|
|
|
|
|
2023-07-05 21:59:49 +00:00
|
|
|
import (
|
|
|
|
"os"
|
2023-07-06 12:10:12 +00:00
|
|
|
"path/filepath"
|
2023-07-06 13:02:50 +00:00
|
|
|
"reflect"
|
2023-07-05 21:59:49 +00:00
|
|
|
"sync"
|
|
|
|
)
|
2023-07-04 22:20:26 +00:00
|
|
|
|
2023-07-05 15:41:53 +00:00
|
|
|
type Collection[T any] interface {
|
2023-07-06 12:10:12 +00:00
|
|
|
All() <-chan *T
|
|
|
|
Clear()
|
2023-07-05 20:02:16 +00:00
|
|
|
Delete(key string)
|
|
|
|
Exists(key string) bool
|
2023-07-06 21:46:47 +00:00
|
|
|
Filter(func(*T) bool) <-chan *T
|
2023-07-06 15:26:19 +00:00
|
|
|
Get(key string) (value *T, err error)
|
2023-07-06 12:10:12 +00:00
|
|
|
Set(key string, value *T)
|
2023-07-08 15:26:36 +00:00
|
|
|
Sync()
|
2023-07-04 22:20:26 +00:00
|
|
|
}
|
|
|
|
|
2023-07-05 15:23:50 +00:00
|
|
|
// collection is a hash map of homogeneous data.
|
2023-07-05 15:41:53 +00:00
|
|
|
type collection[T any] struct {
|
2023-07-07 16:05:52 +00:00
|
|
|
data sync.Map
|
|
|
|
storage Storage[T]
|
|
|
|
name string
|
|
|
|
root string
|
2023-07-04 22:20:26 +00:00
|
|
|
}
|
|
|
|
|
2023-07-06 12:27:48 +00:00
|
|
|
// New creates a new collection with the given name.
|
2023-07-12 08:58:20 +00:00
|
|
|
func New[T any](namespace string, storage Storage[T]) (Collection[T], error) {
|
2023-07-06 13:34:57 +00:00
|
|
|
name := reflect.TypeOf((*T)(nil)).Elem().Name()
|
2023-07-10 14:27:41 +00:00
|
|
|
c := &collection[T]{
|
|
|
|
name: name,
|
|
|
|
}
|
|
|
|
|
|
|
|
if storage == nil {
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2023-07-05 21:59:49 +00:00
|
|
|
home, err := os.UserHomeDir()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-07-10 14:27:41 +00:00
|
|
|
c.storage = storage
|
|
|
|
c.root = filepath.Join(home, ".ocean", namespace)
|
|
|
|
err = os.MkdirAll(c.root, 0700)
|
2023-07-05 21:59:49 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-07-10 14:27:41 +00:00
|
|
|
return c, storage.Init(c)
|
2023-07-06 12:10:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2023-07-05 15:23:50 +00:00
|
|
|
}
|
|
|
|
|
2023-07-08 15:26:36 +00:00
|
|
|
// 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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-07-10 14:27:41 +00:00
|
|
|
// Data returns the internal data structure.
|
|
|
|
func (c *collection[T]) Data() *sync.Map {
|
|
|
|
return &c.data
|
|
|
|
}
|
|
|
|
|
2023-07-08 15:26:36 +00:00
|
|
|
// Delete deletes a key from the collection.
|
|
|
|
func (c *collection[T]) Delete(key string) {
|
|
|
|
c.data.Delete(key)
|
2023-07-10 14:27:41 +00:00
|
|
|
|
|
|
|
if c.storage == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-07-12 08:58:20 +00:00
|
|
|
err := c.storage.Delete(key)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-07-08 15:26:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2023-07-06 21:46:47 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2023-07-10 14:27:41 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2023-07-05 15:23:50 +00:00
|
|
|
// Set sets the value for the given key.
|
2023-07-06 12:10:12 +00:00
|
|
|
func (c *collection[T]) Set(key string, value *T) {
|
2023-07-05 15:23:50 +00:00
|
|
|
c.data.Store(key, value)
|
2023-07-10 14:27:41 +00:00
|
|
|
|
|
|
|
if c.storage == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-07-07 16:05:52 +00:00
|
|
|
err := c.storage.Set(key, value)
|
2023-07-06 13:40:25 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2023-07-06 12:10:12 +00:00
|
|
|
}
|
2023-07-05 21:59:49 +00:00
|
|
|
|
2023-07-08 15:26:36 +00:00
|
|
|
// Sync waits for all disk writes to finish before it returns.
|
|
|
|
func (c *collection[T]) Sync() {
|
2023-07-10 14:27:41 +00:00
|
|
|
if c.storage == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-07-08 15:26:36 +00:00
|
|
|
c.storage.Sync()
|
2023-07-06 12:10:12 +00:00
|
|
|
}
|