local.go

 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}