forked from verless/verless
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfs.go
More file actions
152 lines (126 loc) · 3.42 KB
/
fs.go
File metadata and controls
152 lines (126 loc) · 3.42 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Package fs provides functions for filesystem operations.
package fs
import (
"io"
"os"
"path/filepath"
"strings"
"github.com/spf13/afero"
)
var (
// MarkdownOnly is a filter that only lets pass Markdown files.
MarkdownOnly = func(file string) bool {
return filepath.Ext(file) == ".md"
}
// NoUnderscores is a predefined filter that doesn't let pass
// files starting with an underscore.
NoUnderscores = func(file string) bool {
filename := filepath.Base(file)
return !strings.HasPrefix(filename, "_")
}
// ErrStreaming is returned from StreamFiles.
ErrStreaming error = nil
)
// StreamFiles sends all relative file paths inside a given path that
// match the given filters through the files channel.
func StreamFiles(path string, files chan<- string, filters ...func(file string) bool) error {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
close(files)
return nil
}
return err
}
// Convert to absolute path so that it does not make a difference if
// the paths are in different formats. e.g. one "example/" and the
// other one "./example"
path, err := filepath.Abs(path)
if err != nil {
return err
}
ErrStreaming = filepath.Walk(path, func(file string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
for _, filter := range filters {
if !filter(file) {
return nil
}
}
// Convert to absolute path so that it does not make a difference if
// the paths are in different formats. e.g. one "example/" and the
// other one "./example"
file, err = filepath.Abs(file)
if err != nil {
return err
}
files <- file[len(path):]
return nil
})
close(files)
return ErrStreaming
}
// Rmdir removes an entire directory along with its contents. If the
// directory does not exist, nothing happens.
func Rmdir(targetFs afero.Fs, path string) error {
if _, err := targetFs.Stat(path); !os.IsNotExist(err) {
return err
}
return targetFs.RemoveAll(path)
}
// CopyFromOS copies a given directory from the OS filesystem into
// another filesystem instance to the desired destination.
//
// If fileOnly is set to true, files will be copied directly into the
// destination directory without their directory structure inside src.
func CopyFromOS(targetFs afero.Fs, src, dest string, fileOnly bool) error {
var (
files = make(chan string)
errchan = make(chan error)
err error
)
go func() {
err := StreamFiles(src, files)
errchan <- err
}()
for file := range files {
var destPath, srcPath string
// ToDo: Check if joining the filepath is okay in terms of performance.
srcPath = filepath.Join(src, file)
if fileOnly {
filename := filepath.Base(file)
destPath = filepath.ToSlash(filepath.Join(dest, filename))
} else {
destPath = filepath.ToSlash(filepath.Join(dest, file))
}
if err := targetFs.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
return err
}
srcFile, err := os.Open(srcPath)
if err != nil {
return err
}
destFile, err := targetFs.Create(destPath)
if err != nil {
return err
}
if _, err = io.Copy(destFile, srcFile); err != nil {
return err
}
_ = srcFile.Close()
_ = destFile.Close()
}
err = <-errchan
return err
}
// IsSafeToRemove determines if a directory can be removed safely.
func IsSafeToRemove(targetFs afero.Fs, path string, force bool) bool {
if force {
return true
}
_, err := targetFs.Stat(path)
return os.IsNotExist(err)
}