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) error {
45 name = l.fixPath(name)
46 if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil {
47 return err
48 }
49
50 f, err := os.Create(name)
51 if err != nil {
52 return err
53 }
54 defer f.Close() // nolint: errcheck
55 _, err = io.Copy(f, r)
56 return err
57}
58
59// Exists implements Storage.
60func (l *LocalStorage) Exists(name string) (bool, error) {
61 name = l.fixPath(name)
62 _, err := os.Stat(name)
63 if err == nil {
64 return true, nil
65 }
66 if errors.Is(err, fs.ErrNotExist) {
67 return false, nil
68 }
69 return false, err
70}
71
72// Rename implements Storage.
73func (l *LocalStorage) Rename(oldName, newName string) error {
74 oldName = l.fixPath(oldName)
75 newName = l.fixPath(newName)
76 if err := os.MkdirAll(filepath.Dir(newName), os.ModePerm); err != nil {
77 return err
78 }
79
80 return os.Rename(oldName, newName)
81}
82
83// Replace all slashes with the OS-specific separator
84func (l LocalStorage) fixPath(path string) string {
85 path = strings.ReplaceAll(path, "/", string(os.PathSeparator))
86 if !filepath.IsAbs(path) {
87 return filepath.Join(l.root, path)
88 }
89
90 return path
91}