1// Package storage provides storage functionality.
2package storage
3
4import (
5 "errors"
6 "io"
7 "io/fs"
8 "os"
9 "path/filepath"
10 "strings"
11)
12
13// LocalStorage is a storage implementation that stores objects on the local
14// filesystem.
15type LocalStorage struct {
16 root string
17}
18
19var _ Storage = (*LocalStorage)(nil)
20
21// NewLocalStorage creates a new LocalStorage.
22func NewLocalStorage(root string) *LocalStorage {
23 return &LocalStorage{root: root}
24}
25
26// Delete implements Storage.
27func (l *LocalStorage) Delete(name string) error {
28 name = l.fixPath(name)
29 return os.Remove(name) //nolint:wrapcheck
30}
31
32// Open implements Storage.
33func (l *LocalStorage) Open(name string) (Object, error) {
34 name = l.fixPath(name)
35 return os.Open(name) //nolint:wrapcheck
36}
37
38// Stat implements Storage.
39func (l *LocalStorage) Stat(name string) (fs.FileInfo, error) {
40 name = l.fixPath(name)
41 return os.Stat(name) //nolint:wrapcheck
42}
43
44// Put implements Storage.
45func (l *LocalStorage) Put(name string, r io.Reader) (int64, error) {
46 name = l.fixPath(name)
47 if err := os.MkdirAll(filepath.Dir(name), os.ModePerm); err != nil { //nolint:gosec
48 return 0, err //nolint:wrapcheck
49 }
50
51 f, err := os.Create(name)
52 if err != nil {
53 return 0, err //nolint:wrapcheck
54 }
55 defer f.Close() //nolint:errcheck
56 return io.Copy(f, r) //nolint:wrapcheck
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 //nolint:wrapcheck
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 { //nolint:gosec
77 return err //nolint:wrapcheck
78 }
79
80 return os.Rename(oldName, newName) //nolint:wrapcheck
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}