-
Notifications
You must be signed in to change notification settings - Fork 0
/
fs.go
137 lines (124 loc) · 4.43 KB
/
fs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package filestore
import (
"io"
"io/fs"
"path/filepath"
"strings"
)
// ReaderFile encapsulates a file within a file system that you can read from.
type ReaderFile interface {
io.ReadCloser
io.ReaderAt
io.Seeker
}
// WriterFile encapsulates a file within a file system that you can write to.
type WriterFile interface {
io.WriteCloser
io.WriterAt
io.Seeker
}
// FileInfo contains 'stat' info about a file or directory.
type FileInfo fs.FileInfo
// FS represents a file system that you can interact with its directories and files.
type FS interface {
// WorkingDirectory returns the current FS context's path/directory.
WorkingDirectory() string
// Stat fetches metadata about the file w/o actually opening it for reading/writing.
Stat(path string) (FileInfo, error)
// Read opens the given file for reading.
Read(path string) (ReaderFile, error)
// Write opens the given file for writing
Write(path string) (WriterFile, error)
// Exists returns true when the file/directory already exits in the file system.
Exists(path string) bool
// List performs a UNIX style "ls" operation, giving you the names of each file
// in the given directory. The filters offer a way to limit which files/dirs are included
// in the final slice.
//
// Example:
//
// filesAndDirs, err := myFS.List("./conf")
// jsonFiles, err := myFS.List("./conf", filestore.WithExt("json"))
List(path string, filters ...FileFilter) ([]FileInfo, error)
// ChangeDirectory creates a new FS in the given subdirectory. All operations on this new
// instance will be rooted in the given directory.
//
// It should NOT matter if the directory exists or not. You still always get a valid FS
// pointing to that location and only get an error when you attempt to perform some other operation.
//
// Example:
//
// usrFS := Disk("/usr")
// usrLocalBinFS := usrFS.ChangeDirectory("local/bin")
ChangeDirectory(path string) FS
// Remove deletes the given file/directory within the file system. If the given path
// is a directory, it should recursively delete it and its children. Additionally,
// if you attempt to remove a file/directory that does not exist, this should behave
// quietly as a nop, returning a nil error but not changing the store's state.
//
// Example:
//
// myDocumentsFS := Disk("/Users/rob/Documents")
// err = myDocumentsFS.Remove("foo.txt")
// if err != nil {
// // could not delete file "foo.txt"
// }
// err = myDocumentsFS.Remove("Pictures")
// if err != nil {
// // could not delete directory "Pictures/"
// }
Remove(path string) error
// Move takes an existing file at the fromPath location and moves it to another
// spot in this file system; the toPath location.
Move(fromPath string, toPath string) error
}
// FileFilter provides a way to exclude files/directories from a list/search.
type FileFilter func(info FileInfo) bool
// WithExt creates a file filter that only accepts files that have a specific extension.
func WithExt(extension string) FileFilter {
// Not specifying any particular extension means you want to allow everything.
if extension == "" || extension == "." {
return WithEverything()
}
// Make comparison case-insensitive and allow you to pass an extension with
// or without the leading "."; basically we'll prepend the "." whether you
// supplied it or not.
extension = strings.ToLower(extension)
extension = strings.TrimPrefix(extension, ".")
extension = "." + extension
return func(f FileInfo) bool {
return strings.HasSuffix(strings.ToLower(f.Name()), extension)
}
}
// WithExts creates a file filter that only accepts files that have one of the given extensions.
func WithExts(extensions ...string) FileFilter {
var filters []FileFilter
for _, extension := range extensions {
filters = append(filters, WithExt(extension))
}
return func(f FileInfo) bool {
for _, filter := range filters {
if filter(f) {
return true
}
}
return false
}
}
// WithPattern only allows files to pass through that match the given glob pattern.
func WithPattern(pattern string) FileFilter {
if pattern == "" {
return WithEverything()
}
return func(f FileInfo) bool {
matched, err := filepath.Match(pattern, f.Name())
return matched && err == nil
}
}
// WithEverything is a dummy non-nil file filter you can use to act as though there are no filters.
// Basically it behaves such that all files match.
func WithEverything() FileFilter {
return func(f FileInfo) bool {
return true
}
}