|
@@ -1,6 +1,7 @@
|
|
|
package file
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"io/fs"
|
|
@@ -12,49 +13,121 @@ import (
|
|
|
"git.andreafazzi.eu/andrea/probo/store"
|
|
|
)
|
|
|
|
|
|
+type IndexDirFunc[T store.Storable, K Storer[T]] func(s *FileStore[T, K]) error
|
|
|
+
|
|
|
var (
|
|
|
ErrorMetaHeaderIsNotPresent = errors.New("Meta header was not found in file.")
|
|
|
|
|
|
- BaseDir = "data"
|
|
|
- QuizzesDir = "quizzes"
|
|
|
- CollectionsDir = "collections"
|
|
|
+ BaseDir = "data"
|
|
|
+ QuizzesDir = "quizzes"
|
|
|
+ CollectionsDir = "collections"
|
|
|
+ ParticipantsDir = "participants"
|
|
|
)
|
|
|
|
|
|
type Storer[T store.Storable] interface {
|
|
|
store.Storer[T]
|
|
|
- // store.FilterStorer[T]
|
|
|
}
|
|
|
|
|
|
-type FileStore[T store.Storable, K Storer[T]] struct {
|
|
|
- Storer K
|
|
|
-
|
|
|
+type FilePathConfig struct {
|
|
|
Dir string
|
|
|
FilePrefix string
|
|
|
FileSuffix string
|
|
|
+}
|
|
|
|
|
|
- MarshalFunc func(K, string, []byte) (T, error)
|
|
|
- UnmarshalFunc func(K, string, T) error
|
|
|
+type FileStoreConfig[T store.Storable, K Storer[T]] struct {
|
|
|
+ FilePathConfig
|
|
|
+ FilepathFunc func(T, *FilePathConfig) string
|
|
|
+ UnmarshalFunc func(K, string, []byte) (T, error)
|
|
|
+ MarshalFunc func(K, string, T) error
|
|
|
+ IndexDirFunc func(*FileStore[T, K]) error
|
|
|
+}
|
|
|
+
|
|
|
+type FileStore[T store.Storable, K Storer[T]] struct {
|
|
|
+ *FileStoreConfig[T, K]
|
|
|
+
|
|
|
+ Storer K
|
|
|
|
|
|
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) {
|
|
|
+func DefaultJSONUnmarshalFunc[T store.Storable, K Storer[T]](s K, filepath string, content []byte) (T, error) {
|
|
|
+ entity := new(T)
|
|
|
+ err := json.Unmarshal(content, &entity)
|
|
|
+ if err != nil {
|
|
|
+ return *entity, err
|
|
|
+ }
|
|
|
+
|
|
|
+ c, err := s.Create(*entity)
|
|
|
+ if err != nil {
|
|
|
+ return c, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return c, nil
|
|
|
+}
|
|
|
+
|
|
|
+func DefaultJSONMarshalFunc[T store.Storable, K Storer[T]](s K, filePath string, entity T) error {
|
|
|
+ jsonData, err := json.Marshal(entity)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ file, err := os.Create(filePath)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ defer file.Close()
|
|
|
+
|
|
|
+ _, err = file.Write(jsonData)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func DefaultIndexDirFunc[T store.Storable, K Storer[T]](s *FileStore[T, K]) 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.UnmarshalFunc(s.Storer, fullPath, content)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ s.SetPath(entity, fullPath)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+func NewFileStore[T store.Storable, K Storer[T]](config *FileStoreConfig[T, K], storer K) (*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),
|
|
|
+ FileStoreConfig: config,
|
|
|
+ Storer: storer,
|
|
|
+ paths: make(map[string]string, 0),
|
|
|
}
|
|
|
|
|
|
err := store.IndexDir()
|
|
@@ -72,9 +145,15 @@ func (s *FileStore[T, K]) Create(entity T) (T, error) {
|
|
|
return e, err
|
|
|
}
|
|
|
|
|
|
- filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
|
|
+ var filePath string
|
|
|
|
|
|
- err = s.UnmarshalFunc(s.Storer, filePath, e)
|
|
|
+ if s.FilepathFunc == nil {
|
|
|
+ filePath = filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
|
|
+ } else {
|
|
|
+ filePath = s.FilepathFunc(entity, &s.FilePathConfig)
|
|
|
+ }
|
|
|
+
|
|
|
+ err = s.MarshalFunc(s.Storer, filePath, e)
|
|
|
if err != nil {
|
|
|
return e, err
|
|
|
}
|
|
@@ -90,15 +169,13 @@ func (s *FileStore[T, K]) Update(entity T, id string) (T, error) {
|
|
|
return e, err
|
|
|
}
|
|
|
|
|
|
- filePath := filepath.Join(s.Dir, fmt.Sprintf("%s_%v%s", s.FilePrefix, e.GetID(), s.FileSuffix))
|
|
|
+ filePath := s.GetPath(e)
|
|
|
|
|
|
- err = s.UnmarshalFunc(s.Storer, filePath, e)
|
|
|
+ err = s.MarshalFunc(s.Storer, filePath, e)
|
|
|
if err != nil {
|
|
|
return e, err
|
|
|
}
|
|
|
|
|
|
- s.SetPath(e, filePath)
|
|
|
-
|
|
|
return e, nil
|
|
|
}
|
|
|
|
|
@@ -125,39 +202,7 @@ func (s *FileStore[T, K]) Delete(id string) (T, error) {
|
|
|
}
|
|
|
|
|
|
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
|
|
|
-
|
|
|
+ return s.IndexDirFunc(s)
|
|
|
}
|
|
|
|
|
|
func (s *FileStore[T, K]) GetPath(entity T) string {
|