Nigiri/collection.go
2025-08-15 16:37:17 -05:00

196 lines
4.4 KiB
Go

package nigiri
import (
"fmt"
"os"
"path/filepath"
"sync"
)
// StoreManager interface for stores that can load/save
type StoreManager interface {
LoadData(path string) error
SaveData(path string) error
Clear()
}
// Collection manages multiple stores in a directory
type Collection struct {
baseDir string
stores map[string]StoreManager
mu sync.RWMutex
}
// NewCollection creates a new collection in the specified directory
func NewCollection(baseDir string) *Collection {
return &Collection{
baseDir: baseDir,
stores: make(map[string]StoreManager),
}
}
// Register adds a store to the collection with a given name
func (c *Collection) Register(name string, store StoreManager) {
c.mu.Lock()
defer c.mu.Unlock()
c.stores[name] = store
}
// Unregister removes a store from the collection
func (c *Collection) Unregister(name string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.stores, name)
}
// GetStore retrieves a registered store by name
func (c *Collection) GetStore(name string) (StoreManager, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
store, exists := c.stores[name]
return store, exists
}
// LoadAll loads all registered stores from their JSON files
func (c *Collection) LoadAll() error {
c.mu.RLock()
defer c.mu.RUnlock()
if err := os.MkdirAll(c.baseDir, 0755); err != nil {
return fmt.Errorf("failed to create base directory: %w", err)
}
var firstError error
for name, store := range c.stores {
path := filepath.Join(c.baseDir, name+".json")
if err := store.LoadData(path); err != nil && firstError == nil {
firstError = fmt.Errorf("failed to load %s: %w", name, err)
}
}
return firstError
}
// SaveAll saves all registered stores to their JSON files
func (c *Collection) SaveAll() error {
c.mu.RLock()
defer c.mu.RUnlock()
if err := os.MkdirAll(c.baseDir, 0755); err != nil {
return fmt.Errorf("failed to create base directory: %w", err)
}
var firstError error
for name, store := range c.stores {
path := filepath.Join(c.baseDir, name+".json")
if err := store.SaveData(path); err != nil && firstError == nil {
firstError = fmt.Errorf("failed to save %s: %w", name, err)
}
}
return firstError
}
// LoadStore loads a specific store by name
func (c *Collection) LoadStore(name string) error {
c.mu.RLock()
defer c.mu.RUnlock()
store, exists := c.stores[name]
if !exists {
return fmt.Errorf("store %s not registered", name)
}
path := filepath.Join(c.baseDir, name+".json")
return store.LoadData(path)
}
// SaveStore saves a specific store by name
func (c *Collection) SaveStore(name string) error {
c.mu.RLock()
defer c.mu.RUnlock()
store, exists := c.stores[name]
if !exists {
return fmt.Errorf("store %s not registered", name)
}
if err := os.MkdirAll(c.baseDir, 0755); err != nil {
return fmt.Errorf("failed to create base directory: %w", err)
}
path := filepath.Join(c.baseDir, name+".json")
return store.SaveData(path)
}
// ClearAll clears all registered stores
func (c *Collection) ClearAll() {
c.mu.RLock()
defer c.mu.RUnlock()
for _, store := range c.stores {
store.Clear()
}
}
// ClearStore clears a specific store by name
func (c *Collection) ClearStore(name string) error {
c.mu.RLock()
defer c.mu.RUnlock()
store, exists := c.stores[name]
if !exists {
return fmt.Errorf("store %s not registered", name)
}
store.Clear()
return nil
}
// ListStores returns names of all registered stores
func (c *Collection) ListStores() []string {
c.mu.RLock()
defer c.mu.RUnlock()
names := make([]string, 0, len(c.stores))
for name := range c.stores {
names = append(names, name)
}
return names
}
// GetPath returns the file path for a store name
func (c *Collection) GetPath(name string) string {
return filepath.Join(c.baseDir, name+".json")
}
// StoreExists checks if a store file exists on disk
func (c *Collection) StoreExists(name string) bool {
path := c.GetPath(name)
_, err := os.Stat(path)
return err == nil
}
// RemoveStoreFile removes the JSON file for a store
func (c *Collection) RemoveStoreFile(name string) error {
path := c.GetPath(name)
err := os.Remove(path)
if os.IsNotExist(err) {
return nil // Already doesn't exist
}
return err
}
// Helper function to create typed store accessor
func GetTypedStore[T any](collection *Collection, name string) (*BaseStore[T], bool) {
store, exists := collection.GetStore(name)
if !exists {
return nil, false
}
if typedStore, ok := store.(*BaseStore[T]); ok {
return typedStore, true
}
return nil, false
}