Implemented storage interface

This commit is contained in:
Eduard Urbach 2023-07-07 18:05:52 +02:00
parent 8b3914eba5
commit e0a5fa281b
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
4 changed files with 186 additions and 79 deletions

View File

@ -4,7 +4,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"sync" "sync"
) )
@ -21,8 +20,9 @@ type Collection[T any] interface {
// collection is a hash map of homogeneous data. // collection is a hash map of homogeneous data.
type collection[T any] struct { type collection[T any] struct {
data sync.Map data sync.Map
storage Storage[T]
name string name string
directory string root string
} }
// New creates a new collection with the given name. // New creates a new collection with the given name.
@ -35,7 +35,6 @@ func New[T any](directories ...string) (*collection[T], error) {
} }
directories = append([]string{home, ".ocean"}, directories...) directories = append([]string{home, ".ocean"}, directories...)
directories = append(directories, name)
directory := filepath.Join(directories...) directory := filepath.Join(directories...)
err = os.MkdirAll(directory, 0700) err = os.MkdirAll(directory, 0700)
@ -44,11 +43,12 @@ func New[T any](directories ...string) (*collection[T], error) {
} }
c := &collection[T]{ c := &collection[T]{
name: directories[len(directories)-1], name: name,
directory: directory, root: directory,
storage: &DirectoryStorage[T]{},
} }
return c, c.loadFromDisk() return c, c.storage.Init(c)
} }
// All returns a channel of all objects in the collection. // All returns a channel of all objects in the collection.
@ -100,7 +100,7 @@ func (c *collection[T]) Get(key string) (*T, error) {
// Set sets the value for the given key. // Set sets the value for the given key.
func (c *collection[T]) Set(key string, value *T) { func (c *collection[T]) Set(key string, value *T) {
c.data.Store(key, value) c.data.Store(key, value)
err := c.writeFileToDisk(key, value) err := c.storage.Set(key, value)
if err != nil { if err != nil {
panic(err) panic(err)
@ -114,7 +114,7 @@ func (c *collection[T]) Delete(key string) {
} }
c.data.Delete(key) c.data.Delete(key)
os.Remove(c.keyFile(key)) c.storage.Delete(key)
} }
// Exists returns whether or not the key exists. // Exists returns whether or not the key exists.
@ -130,72 +130,3 @@ func (c *collection[T]) Clear() {
return true return true
}) })
} }
// keyFile returns the file path for the given key.
func (c *collection[T]) keyFile(key string) string {
return filepath.Join(c.directory, key+".json")
}
// loadFromDisk loads the collection data from the disk.
func (c *collection[T]) loadFromDisk() error {
dir, err := os.Open(c.directory)
if err != nil {
return err
}
defer dir.Close()
files, err := dir.Readdirnames(0)
for _, key := range files {
fileError := c.loadFileFromDisk(key)
if fileError != nil {
return fileError
}
}
return err
}
// loadFileFromDisk loads a single file from the disk.
func (c *collection[T]) loadFileFromDisk(fileName string) error {
file, err := os.Open(filepath.Join(c.directory, fileName))
if err != nil {
return err
}
value := new(T)
decoder := NewDecoder(file)
err = decoder.Decode(value)
if err != nil {
file.Close()
return err
}
key := strings.TrimSuffix(fileName, ".json")
c.data.Store(key, value)
return file.Close()
}
// writeFileToDisk writes the value for the key to disk as a JSON file.
func (c *collection[T]) writeFileToDisk(key string, value *T) error {
fileName := c.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()
}

100
DirectoryStorage.go Normal file
View File

@ -0,0 +1,100 @@
package ocean
import (
"os"
"path/filepath"
"strings"
)
// DirectoryStorage creates a directory and stores every record in a separate file.
type DirectoryStorage[T any] struct {
collection *collection[T]
directory string
}
// Init loads all existing records from the directory.
func (ds *DirectoryStorage[T]) Init(c *collection[T]) error {
ds.collection = c
ds.directory = filepath.Join(c.root, c.name)
os.Mkdir(ds.directory, 0700)
return ds.loadFromDisk()
}
// Set saves the value in a file.
func (ds *DirectoryStorage[T]) Set(key string, value *T) error {
return ds.writeFileToDisk(key, value)
}
// Delete deletes the file for the given key.
func (ds *DirectoryStorage[T]) Delete(key string) error {
return os.Remove(ds.keyFile(key))
}
// keyFile returns the file path for the given key.
func (ds *DirectoryStorage[T]) keyFile(key string) string {
return filepath.Join(ds.directory, key+".json")
}
// loadFromDisk loads the collection data from the disk.
func (ds *DirectoryStorage[T]) loadFromDisk() 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.loadFileFromDisk(fileName)
if fileError != nil {
return fileError
}
}
return err
}
// loadFileFromDisk loads a single file from the disk.
func (ds *DirectoryStorage[T]) loadFileFromDisk(fileName string) error {
file, err := os.Open(filepath.Join(ds.directory, fileName))
if err != nil {
return err
}
value := new(T)
decoder := NewDecoder(file)
err = decoder.Decode(value)
if err != nil {
file.Close()
return err
}
key := strings.TrimSuffix(fileName, ".json")
ds.collection.data.Store(key, value)
return file.Close()
}
// writeFileToDisk writes the value for the key to disk as a JSON file.
func (ds *DirectoryStorage[T]) writeFileToDisk(key string, value *T) 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()
}

69
FileStorage.go Normal file
View File

@ -0,0 +1,69 @@
package ocean
// import (
// "bufio"
// "encoding/json"
// "io"
// "os"
// "path/filepath"
// )
//
// type FileStorage[T any] struct {
// collection *collection[T]
// dirty chan struct{}
// }
//
// func (fs *FileStorage[T]) Init(c *collection[T]) error {
// fs.collection = c
// fileName := filepath.Join(c.root, c.name+".dat")
// stream, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
//
// if os.IsNotExist(err) {
// return nil
// }
//
// if err != nil {
// return err
// }
//
// defer stream.Close()
// return fs.readRecords(stream)
// }
//
// func (fs *FileStorage[T]) Set(key string, value *T) error {
// return nil
// }
//
// func (fs *FileStorage[T]) Delete(key string) error {
// return nil
// }
//
// // readRecords reads the entire collection.
// func (fs *FileStorage[T]) readRecords(stream io.Reader) error {
// var (
// key string
// value []byte
// )
//
// scanner := bufio.NewScanner(stream)
//
// for scanner.Scan() {
// if key == "" {
// key = scanner.Text()
// continue
// }
//
// value = scanner.Bytes()
// object := new(T)
// err := json.Unmarshal(value, object)
//
// if err != nil {
// return err
// }
//
// fs.collection.data.Store(key, object)
// key = ""
// }
//
// return nil
// }

7
Storage.go Normal file
View File

@ -0,0 +1,7 @@
package ocean
type Storage[T any] interface {
Init(c *collection[T]) error
Set(key string, value *T) error
Delete(key string) error
}