filestore/backend.go
2022-07-03 20:28:50 +02:00

140 lines
3.2 KiB
Go

package filestore
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"path/filepath"
)
// Backend defines the set of methods that a storage backend must implement.
// It is quite naïve and regroups what is used by the Filestore in order to
// handle its operations.
type Backend interface {
// Sub should return a new backend rooted in a subdirectory of the
// original backend.
Sub(path string) Backend
// Create should open a file for writing and return a type that
// implements WriteCloser . The caller MUST call close when the file is
// not needed anymore (typically when all the data has been written).
//
// It should not return an error if the file already exists, but
// instead truncate or replace the existing file.
Create(name string) (io.WriteCloser, error)
// Open should open a file for reading and return a type that
// implements ReadCloser . The caller MUST call close when the file is
// not needed anymore (typically when all the data has been read).
Open(name string) (io.ReadCloser, error)
// Exists should return true if the file exists and false otherwise.
Exists(name string) bool
// Delete should delete the actual file. It should not return an error
// if the file does not exist.
Delete(name string) error
}
type OSBackend struct {
path string
}
func NewOSBackend(path string) *OSBackend {
return &OSBackend{
path: path,
}
}
func (ob OSBackend) Sub(path string) Backend {
return &OSBackend{filepath.Join(ob.path, path)}
}
func (ob OSBackend) Create(name string) (io.WriteCloser, error) {
if err := os.MkdirAll(ob.path, 0o700); err != nil {
return nil, fmt.Errorf("cannot create the directory to write the file: %w", err)
}
file, err := os.OpenFile(filepath.Join(ob.path, name),
os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
if err != nil {
return nil, fmt.Errorf("cannot create file: %w", err)
}
return file, nil
}
func (ob OSBackend) Open(name string) (io.ReadCloser, error) {
file, err := os.Open(filepath.Join(ob.path, name))
if err != nil {
return nil, fmt.Errorf("cannot open file %q: %w", name, err)
}
return file, nil
}
func (ob OSBackend) Exists(name string) bool {
if _, err := os.Stat(filepath.Join(ob.path, name)); !os.IsNotExist(err) {
return true
}
return false
}
func (ob OSBackend) Delete(name string) error {
if !ob.Exists(name) {
return nil
}
if err := os.Remove(filepath.Join(ob.path, name)); err != nil {
return fmt.Errorf("cannot remove file %q: %w", name, err)
}
return nil
}
type MemoryFile struct {
*bytes.Buffer
}
func (MemoryFile) Close() error {
return nil
}
type MemoryBackend struct {
files map[string][]byte
}
func (mb *MemoryBackend) Sub(name string) Backend {
return new(MemoryBackend)
}
func (mb *MemoryBackend) Create(name string) (io.WriteCloser, error) {
if mb.files == nil {
mb.files = map[string][]byte{}
}
mb.files[name] = []byte{}
return MemoryFile{bytes.NewBuffer(mb.files[name])}, nil
}
func (mb *MemoryBackend) Open(name string) (io.ReadCloser, error) {
if b, ok := mb.files[name]; ok {
return MemoryFile{bytes.NewBuffer(b)}, nil
}
return nil, errors.New("file not found")
}
func (mb *MemoryBackend) Delete(name string) error {
delete(mb.files, name)
return nil
}
func (mb *MemoryBackend) Exists(name string) bool {
_, ok := mb.files[name]
return ok
}