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 }