probo/store/file/file.go

220 lines
3.7 KiB
Go
Raw Normal View History

2023-06-28 17:21:59 +02:00
package file
import (
"errors"
2023-11-13 21:01:12 +01:00
"fmt"
"io/fs"
"os"
2023-06-28 17:21:59 +02:00
"path/filepath"
2023-11-13 21:01:12 +01:00
"strings"
2023-07-10 13:23:46 +02:00
"sync"
2023-06-28 17:21:59 +02:00
2023-11-13 21:01:12 +01:00
"git.andreafazzi.eu/andrea/probo/store"
2023-06-28 17:21:59 +02:00
)
2023-11-20 14:14:09 +01:00
type IndexDirFunc[T FileStorable, K Storer[T]] func(s *FileStore[T, K]) error
2023-10-07 11:43:12 +02:00
var (
ErrorMetaHeaderIsNotPresent = errors.New("Meta header was not found in file.")
)
2023-09-22 10:29:10 +02:00
2023-11-20 14:14:09 +01:00
type FileStorable interface {
store.Storable
Marshal() ([]byte, error)
Unmarshal([]byte) error
}
2023-11-13 21:01:12 +01:00
type Storer[T store.Storable] interface {
store.Storer[T]
}
2023-06-28 17:21:59 +02:00
type FilePathConfig struct {
2023-11-13 21:01:12 +01:00
Dir string
FilePrefix string
FileSuffix string
}
2023-10-07 11:43:12 +02:00
2023-11-20 14:14:09 +01:00
type FileStoreConfig[T FileStorable, K Storer[T]] struct {
FilePathConfig
2023-11-21 18:24:10 +01:00
IndexDirFunc func(*FileStore[T, K]) error
CreateEntityFunc func() T
NoIndexOnCreate bool
}
2023-11-20 14:14:09 +01:00
type FileStore[T FileStorable, K Storer[T]] struct {
*FileStoreConfig[T, K]
Storer K
2023-07-10 13:23:46 +02:00
2023-11-13 21:01:12 +01:00
lock sync.RWMutex
paths map[string]string
2023-06-28 17:21:59 +02:00
}
2023-11-20 14:14:09 +01:00
func DefaultIndexDirFunc[T FileStorable, K Storer[T]](s *FileStore[T, K]) error {
2023-11-21 18:24:10 +01:00
if s.CreateEntityFunc == nil {
return errors.New("CreateEntityFunc cannot be nil!")
}
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
}
2023-11-21 18:24:10 +01:00
entity := s.CreateEntityFunc()
2023-11-20 14:14:09 +01:00
err = entity.Unmarshal(content)
if err != nil {
return err
}
2023-11-28 16:19:49 +01:00
mEntity, err := s.Create(entity, fullPath)
if err != nil {
return err
}
2023-11-20 14:14:09 +01:00
s.SetPath(mEntity, fullPath)
}
return nil
}
2023-11-20 14:14:09 +01:00
func NewFileStore[T FileStorable, K Storer[T]](config *FileStoreConfig[T, K], storer K) (*FileStore[T, K], error) {
2023-11-13 21:01:12 +01:00
store := &FileStore[T, K]{
FileStoreConfig: config,
Storer: storer,
paths: make(map[string]string, 0),
2023-11-13 21:01:12 +01:00
}
2023-11-18 12:01:57 +01:00
if !config.NoIndexOnCreate {
err := store.IndexDir()
if err != nil {
return nil, err
2023-11-13 21:01:12 +01:00
2023-11-18 12:01:57 +01:00
}
2023-11-13 21:01:12 +01:00
}
return store, nil
}
2023-06-28 17:21:59 +02:00
2023-11-28 16:19:49 +01:00
func (s *FileStore[T, K]) Create(entity T, path ...string) (T, error) {
2023-11-13 21:01:12 +01:00
e, err := s.Storer.Create(entity)
if err != nil {
return e, err
}
2023-07-10 13:23:46 +02:00
2023-11-20 14:14:09 +01:00
data, err := e.Marshal()
if err != nil {
return e, err
}
2023-11-28 16:19:49 +01:00
if len(path) == 0 {
path = append(path, filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix)))
}
file, err := os.Create(path[0])
2023-11-20 14:14:09 +01:00
if err != nil {
return e, err
}
defer file.Close()
_, err = file.Write(data)
2023-06-28 17:21:59 +02:00
if err != nil {
2023-11-13 21:01:12 +01:00
return e, err
2023-06-28 17:21:59 +02:00
}
2023-11-28 16:19:49 +01:00
s.SetPath(e, path[0])
2023-11-13 21:01:12 +01:00
return e, nil
2023-07-10 13:23:46 +02:00
}
2023-11-13 21:01:12 +01:00
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
}
2023-10-07 11:43:12 +02:00
filePath := s.GetPath(e)
2023-10-07 11:43:12 +02:00
2023-11-20 14:14:09 +01:00
data, err := e.Marshal()
if err != nil {
return e, err
}
file, err := os.Create(filePath)
if err != nil {
return e, err
}
defer file.Close()
_, err = file.Write(data)
2023-10-07 11:43:12 +02:00
if err != nil {
2023-11-13 21:01:12 +01:00
return e, err
2023-10-07 11:43:12 +02:00
}
2023-11-13 21:01:12 +01:00
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 {
return s.IndexDirFunc(s)
2023-11-13 21:01:12 +01:00
}
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
2023-06-28 17:21:59 +02:00
}