123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package file
- import (
- "errors"
- "fmt"
- "io/fs"
- "os"
- "path/filepath"
- "strings"
- "sync"
- "git.andreafazzi.eu/andrea/probo/store"
- )
- type IndexDirFunc[T FileStorable, K Storer[T]] func(s *FileStore[T, K]) error
- var (
- ErrorMetaHeaderIsNotPresent = errors.New("Meta header was not found in file.")
- )
- type FileStorable interface {
- store.Storable
- Marshal() ([]byte, error)
- Unmarshal([]byte) error
- }
- type Storer[T store.Storable] interface {
- store.Storer[T]
- }
- type FilePathConfig struct {
- Dir string
- FilePrefix string
- FileSuffix string
- }
- type FileStoreConfig[T FileStorable, K Storer[T]] struct {
- FilePathConfig
- IndexDirFunc func(*FileStore[T, K]) error
- CreateEntityFunc func() T
- NoIndexOnCreate bool
- }
- type FileStore[T FileStorable, K Storer[T]] struct {
- *FileStoreConfig[T, K]
- Storer K
- lock sync.RWMutex
- paths map[string]string
- }
- func DefaultIndexDirFunc[T FileStorable, K Storer[T]](s *FileStore[T, K]) error {
- 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
- }
- entity := s.CreateEntityFunc()
- err = entity.Unmarshal(content)
- if err != nil {
- return err
- }
- mEntity, err := s.Create(entity, fullPath)
- if err != nil {
- return err
- }
- s.SetPath(mEntity, fullPath)
- }
- return nil
- }
- func NewFileStore[T FileStorable, K Storer[T]](config *FileStoreConfig[T, K], storer K) (*FileStore[T, K], error) {
- store := &FileStore[T, K]{
- FileStoreConfig: config,
- Storer: storer,
- paths: make(map[string]string, 0),
- }
- if !config.NoIndexOnCreate {
- err := store.IndexDir()
- if err != nil {
- return nil, err
- }
- }
- return store, nil
- }
- func (s *FileStore[T, K]) Create(entity T, path ...string) (T, error) {
- e, err := s.Storer.Create(entity)
- if err != nil {
- return e, err
- }
- data, err := e.Marshal()
- if err != nil {
- return e, err
- }
- 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])
- if err != nil {
- return e, err
- }
- defer file.Close()
- _, err = file.Write(data)
- if err != nil {
- return e, err
- }
- s.SetPath(e, path[0])
- 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 := s.GetPath(e)
- 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)
- if err != nil {
- return e, err
- }
- 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)
- }
- 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
- }
|