1package storage
2
3import (
4 "errors"
5 "io"
6 "io/fs"
7 "os"
8 "path/filepath"
9 "strings"
10)
11
12// LocalStorage is a storage implementation that stores objects on the local
13// filesystem.
14type LocalStorage struct {
15 root string
16}
17
18var _ Storage = (*LocalStorage)(nil)
19
20// NewLocalStorage creates a new LocalStorage.
21func NewLocalStorage(root string) *LocalStorage {
22 return &LocalStorage{root: root}
23}
24
25// Delete implements Storage.
26func (l *LocalStorage) Delete(name string) error {
27 name = l.fixPath(name)
28 return os.Remove(name)
29}
30
31// Open implements Storage.
32func (l *LocalStorage) Open(name string) (Object, error) {
33 name = l.fixPath(name)
34 return os.Open(name)
35}
36
37// Stat implements Storage.
38func (l *LocalStorage) Stat(name string) (fs.FileInfo, error) {
39 name = l.fixPath(name)
40 return os.Stat(name)
41}
42
43// 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, err
48 }
49
50 f, err := os.Create(name)
51 if err != nil {
52 return 0, err
53 }
54 defer f.Close()
55 return io.Copy(f, r)
56}
57
58// 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, nil
64 }
65 if errors.Is(err, fs.ErrNotExist) {
66 return false, nil
67 }
68 return false, err
69}
70
71// 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 err
77 }
78
79 return os.Rename(oldName, newName)
80}
81
82// Replace all slashes with the OS-specific separator.
83func (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 }
88
89 return path
90}