package ocean import ( "os" "path/filepath" "reflect" "sync" ) 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, ".ocean", namespace) err = os.MkdirAll(c.root, 0700) if err != nil { return nil, err } return c, storage.Init(c) } // 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() }