140 lines
3.2 KiB
Go
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
|
|
}
|