probo/store/file/file.go

177 lines
3.2 KiB
Go

package file
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"sync"
"git.andreafazzi.eu/andrea/probo/store"
)
var (
ErrorMetaHeaderIsNotPresent = errors.New("Meta header was not found in file.")
BaseDir = "data"
QuizzesDir = "quizzes"
CollectionsDir = "collections"
)
type Storer[T store.Storable] interface {
store.Storer[T]
// store.FilterStorer[T]
}
type FileStore[T store.Storable, K Storer[T]] struct {
Storer K
Dir string
FilePrefix string
FileSuffix string
MarshalFunc func(K, string, []byte) (T, error)
UnmarshalFunc func(K, string, T) error
lock sync.RWMutex
paths map[string]string
}
func NewFileStore[T store.Storable, K Storer[T]](
storer K,
dir string,
prefix string,
suffix string,
marshalFunc func(K, string, []byte) (T, error),
unmarshalFunc func(K, string, T) error,
) (*FileStore[T, K], error) {
store := &FileStore[T, K]{
Storer: storer,
Dir: dir,
FilePrefix: prefix,
FileSuffix: suffix,
MarshalFunc: marshalFunc,
UnmarshalFunc: unmarshalFunc,
paths: make(map[string]string, 0),
}
err := store.IndexDir()
if err != nil {
return nil, err
}
return store, nil
}
func (s *FileStore[T, K]) Create(entity T) (T, error) {
e, err := s.Storer.Create(entity)
if err != nil {
return e, err
}
filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
err = s.UnmarshalFunc(s.Storer, filePath, e)
if err != nil {
return e, err
}
s.SetPath(e, filePath)
return e, nil
}
func (s *FileStore[T, K]) Update(entity T, id string) (T, error) {
e, err := s.Storer.Update(entity, id)
if err != nil {
return e, err
}
filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
err = s.UnmarshalFunc(s.Storer, filePath, e)
if err != nil {
return e, err
}
s.SetPath(e, filePath)
return e, nil
}
func (s *FileStore[T, K]) Read(id string) (T, error) {
return s.Storer.Read(id)
}
func (s *FileStore[T, K]) ReadAll() []T {
return s.Storer.ReadAll()
}
func (s *FileStore[T, K]) Delete(id string) (T, error) {
e, err := s.Storer.Delete(id)
if err != nil {
return e, err
}
err = os.Remove(s.GetPath(e))
if err != nil {
return e, err
}
return e, nil
}
func (s *FileStore[T, K]) IndexDir() error {
files, err := os.ReadDir(s.Dir)
if err != nil {
return err
}
entityFiles := make([]fs.DirEntry, 0)
for _, file := range files {
filename := file.Name()
if !file.IsDir() && strings.HasSuffix(filename, s.FileSuffix) {
entityFiles = append(entityFiles, file)
}
}
for _, file := range entityFiles {
filename := file.Name()
fullPath := filepath.Join(s.Dir, filename)
content, err := os.ReadFile(fullPath)
if err != nil {
return err
}
entity, err := s.MarshalFunc(s.Storer, fullPath, content)
if err != nil {
return err
}
s.SetPath(entity, fullPath)
}
return nil
}
func (s *FileStore[T, K]) GetPath(entity T) string {
s.lock.RLock()
defer s.lock.RUnlock()
return s.paths[entity.GetID()]
}
func (s *FileStore[T, K]) SetPath(entity T, path string) string {
s.lock.Lock()
defer s.lock.Unlock()
s.paths[entity.GetID()] = path
return path
}