1package storage23import (4 "errors"5 "io"6 "io/fs"7 "os"8 "path/filepath"9 "strings"10)1112// LocalStorage is a storage implementation that stores objects on the local13// filesystem.14type LocalStorage struct {15 root string16}1718var _ Storage = (*LocalStorage)(nil)1920// NewLocalStorage creates a new LocalStorage.21func NewLocalStorage(root string) *LocalStorage {22 return &LocalStorage{root: root}23}2425// Delete implements Storage.26func (l *LocalStorage) Delete(name string) error {27 name = l.fixPath(name)28 return os.Remove(name)29}3031// Open implements Storage.32func (l *LocalStorage) Open(name string) (Object, error) {33 name = l.fixPath(name)34 return os.Open(name)35}3637// Stat implements Storage.38func (l *LocalStorage) Stat(name string) (fs.FileInfo, error) {39 name = l.fixPath(name)40 return os.Stat(name)41}4243// Put implements Storage.44func (l *LocalStorage) Put(name string, r io.Reader) (int64, error) {45 name = l.fixPath(name)46 if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil {47 return 0, err48 }4950 f, err := os.Create(name)51 if err != nil {52 return 0, err53 }54 defer f.Close() // nolint: errcheck55 return io.Copy(f, r)56}5758// Exists implements Storage.59func (l *LocalStorage) Exists(name string) (bool, error) {60 name = l.fixPath(name)61 _, err := os.Stat(name)62 if err == nil {63 return true, nil64 }65 if errors.Is(err, fs.ErrNotExist) {66 return false, nil67 }68 return false, err69}7071// Rename implements Storage.72func (l *LocalStorage) Rename(oldName, newName string) error {73 oldName = l.fixPath(oldName)74 newName = l.fixPath(newName)75 if err := os.MkdirAll(filepath.Dir(newName), os.ModePerm); err != nil {76 return err77 }7879 return os.Rename(oldName, newName)80}8182// Replace all slashes with the OS-specific separator83func (l LocalStorage) fixPath(path string) string {84 path = strings.ReplaceAll(path, "/", string(os.PathSeparator))85 if !filepath.IsAbs(path) {86 return filepath.Join(l.root, path)87 }8889 return path90}