1 package storage
2
3 import (
4 "context"
5 "fmt"
6 "io"
7 "os"
8 "path/filepath"
9 "strings"
10 )
11
12
13
14 const DefaultLocalCreatePathMode = os.FileMode(0o755)
15
16
17 var LocalCreatePathMode = DefaultLocalCreatePathMode
18
19
20 type localFS string
21
22 func NewLocalFS(path string) FS {
23 fs := localFS(path)
24
25 return &fs
26 }
27
28 func (l *localFS) fullPath(path string) string {
29 return filepath.Join(string(*l), path)
30 }
31
32 func (l *localFS) wrapError(path string, err error) error {
33 if os.IsNotExist(err) {
34 return ¬ExistError{
35 Path: path,
36 }
37 }
38
39 return err
40 }
41
42
43 func (l *localFS) Open(_ context.Context, path string, _ *ReaderOptions) (*File, error) {
44 path = l.fullPath(path)
45
46 f, err := os.Open(path)
47 if err != nil {
48 return nil, l.wrapError(path, err)
49 }
50
51 stat, err := f.Stat()
52 if err != nil {
53 return nil, l.wrapError(path, err)
54 }
55
56 return &File{
57 ReadCloser: f,
58 Attributes: Attributes{
59 ModTime: stat.ModTime(),
60 Size: stat.Size(),
61 },
62 }, nil
63 }
64
65
66 func (l *localFS) Attributes(_ context.Context, path string, _ *ReaderOptions) (*Attributes, error) {
67 path = l.fullPath(path)
68
69 stat, err := os.Stat(path)
70 if err != nil {
71 return nil, l.wrapError(path, err)
72 }
73
74 return &Attributes{
75 ModTime: stat.ModTime(),
76 Size: stat.Size(),
77 }, nil
78 }
79
80
81
82 func (l *localFS) Create(_ context.Context, path string, options *WriterOptions) (io.WriteCloser, error) {
83 path = l.fullPath(path)
84
85 dir := filepath.Dir(path)
86 if _, err := os.Stat(dir); os.IsNotExist(err) {
87 if err := os.MkdirAll(dir, LocalCreatePathMode); err != nil {
88 return nil, err
89 }
90 }
91
92 f, err := os.Create(path)
93 if err != nil {
94 return nil, err
95 }
96 if options != nil {
97 if !options.Attributes.CreationTime.IsZero() {
98
99
100 options.Attributes.ModTime = options.Attributes.CreationTime
101 }
102 if !options.Attributes.ModTime.IsZero() {
103 if err := os.Chtimes(path, options.Attributes.ModTime, options.Attributes.ModTime); err != nil {
104 return f, err
105 }
106 }
107 }
108
109 return f, nil
110 }
111
112
113 func (l *localFS) Delete(_ context.Context, path string) error {
114 return os.RemoveAll(l.fullPath(path))
115 }
116
117
118 func (l *localFS) Walk(_ context.Context, path string, fn WalkFn) error {
119 return filepath.Walk(l.fullPath(path), func(path string, f os.FileInfo, err error) error {
120 if err != nil {
121 return err
122 }
123
124 if !f.IsDir() {
125 path = strings.TrimPrefix(path, string(*l))
126
127 return fn(path)
128 }
129
130 return nil
131 })
132 }
133
134 func (l *localFS) URL(_ context.Context, path string, _ *SignedURLOptions) (string, error) {
135 path = l.fullPath(path)
136 _, err := os.Stat(path)
137 if err != nil {
138 return "", l.wrapError(path, err)
139 }
140
141 return fmt.Sprintf("file://%s", path), nil
142 }
143
View as plain text