package nigiri import ( "fmt" "os" "path/filepath" "regexp" "sync" ) type StoreManager interface { LoadData(path string) error SaveData(path string) error Clear() EntityExists(id int) bool SetValidator(validator any) } type WALLogger interface { LogOperation(store, operation string, id int, data any) } type Collection struct { baseDir string stores map[string]StoreManager migrator *Migrator wal *WAL mu sync.RWMutex } // ============================================================================ // Core Collection Management // ============================================================================ func NewCollection(baseDir string) *Collection { return &Collection{ baseDir: baseDir, stores: make(map[string]StoreManager), } } func (c *Collection) Close() error { if err := c.Checkpoint(); err != nil { return err } if c.wal != nil { return c.wal.close() } return nil } // ============================================================================ // Store Management // ============================================================================ func (c *Collection) Add(name string, store StoreManager) error { if err := c.ensureWAL(); err != nil { return err } c.mu.Lock() defer c.mu.Unlock() c.stores[name] = store store.SetValidator(c) if walStore, ok := store.(interface{ SetWALLogger(WALLogger, string) }); ok { walStore.SetWALLogger(c, name) } path := filepath.Join(c.baseDir, name+".json") if _, err := os.Stat(path); err == nil { return store.LoadData(path) } return nil } func Get[T any](c *Collection, name string) *BaseStore[T] { c.mu.RLock() defer c.mu.RUnlock() if store, exists := c.stores[name]; exists { if typed, ok := store.(*BaseStore[T]); ok { return typed } } return nil } // ============================================================================ // Persistence Operations // ============================================================================ func (c *Collection) Load() error { c.mu.RLock() defer c.mu.RUnlock() os.MkdirAll(c.baseDir, 0755) for name, store := range c.stores { path := filepath.Join(c.baseDir, name+".json") if err := store.LoadData(path); err != nil { return fmt.Errorf("load %s: %w", name, err) } } return nil } func (c *Collection) Save() error { c.mu.RLock() defer c.mu.RUnlock() os.MkdirAll(c.baseDir, 0755) for name, store := range c.stores { path := filepath.Join(c.baseDir, name+".json") if err := store.SaveData(path); err != nil { return fmt.Errorf("save %s: %w", name, err) } } return nil } func (c *Collection) Clear() { c.mu.RLock() defer c.mu.RUnlock() for _, store := range c.stores { store.Clear() } } func (c *Collection) Checkpoint() error { if c.wal != nil { if err := c.wal.flush(); err != nil { return err } } if err := c.Save(); err != nil { return err } if c.wal != nil { return c.wal.truncate() } return nil } // ============================================================================ // Validation & Relationships // ============================================================================ func (c *Collection) EntityExists(entityName string, id int) bool { c.mu.RLock() defer c.mu.RUnlock() if store, exists := c.stores[entityName]; exists { return store.EntityExists(id) } return false } // ============================================================================ // WAL Operations // ============================================================================ func (c *Collection) LogOperation(store, operation string, id int, data any) { if c.wal != nil { c.wal.logOperation(store, operation, id, data) } } func (c *Collection) ensureWAL() error { if c.wal != nil { return nil } wal, err := newWAL(c.baseDir) if err != nil { return err } c.wal = wal return wal.recover(c) } // ============================================================================ // Migration Operations // ============================================================================ func (c *Collection) GetMigrator() *Migrator { if c.migrator == nil { c.migrator = NewMigrator() } return c.migrator } func (c *Collection) RegisterMigrationCommand(name string, pattern *regexp.Regexp, handler MigrationHandler) { c.GetMigrator().RegisterCommand(name, pattern, handler) } func (c *Collection) MigrateStore(storeName, command string) error { c.mu.Lock() defer c.mu.Unlock() path := filepath.Join(c.baseDir, storeName+".json") return c.GetMigrator().MigrateFile(path, command) } func (c *Collection) RunMigrationScript(scriptFile string) error { return c.GetMigrator().RunScript(c.baseDir, scriptFile) } func (c *Collection) CreateMigrationCLI() *MigrationCLI { return NewMigrationCLI(c) }