package storage import ( "os" "path/filepath" "strings" "git.akyoto.dev/go/ocean" ) // Directory creates a directory and stores every record in a separate file. type Directory[T any] struct { collection ocean.StorageData directory string } // Init loads all existing records from the directory. func (ds *Directory[T]) Init(c ocean.StorageData) error { ds.collection = c ds.directory = filepath.Join(c.Root(), c.Name()) err := os.Mkdir(ds.directory, 0700) if err != nil && !os.IsExist(err) { return err } return ds.read() } // Set saves the value in a file. func (ds *Directory[T]) Set(key string, value *T) error { return ds.writeFile(key, value) } // Delete deletes the file for the given key. func (ds *Directory[T]) Delete(key string) error { err := os.Remove(ds.keyFile(key)) if os.IsNotExist(err) { return nil } return err } // Sync does nothing when using directory storage. func (ds *Directory[T]) Sync() {} // read loads the collection data from the disk. func (ds *Directory[T]) read() error { dir, err := os.Open(ds.directory) if err != nil { return err } defer dir.Close() files, err := dir.Readdirnames(0) for _, fileName := range files { fileError := ds.readFile(fileName) if fileError != nil { return fileError } } return err } // readFile loads a single file from the disk. func (ds *Directory[T]) readFile(fileName string) error { filePath := filepath.Join(ds.directory, fileName) file, err := os.Open(filePath) if err != nil { return err } defer file.Close() value := new(T) decoder := NewDecoder(file) err = decoder.Decode(value) if err != nil { return err } key := strings.TrimSuffix(fileName, ".json") ds.collection.Data().Store(key, value) return nil } // writeFile writes the value for the key to disk as a JSON file. func (ds *Directory[T]) writeFile(key string, value any) error { fileName := ds.keyFile(key) file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600) if err != nil { return err } encoder := NewEncoder(file) err = encoder.Encode(value) if err != nil { file.Close() return err } return file.Close() } // keyFile returns the file path for the given key. func (ds *Directory[T]) keyFile(key string) string { return filepath.Join(ds.directory, key+".json") }