/
filesystem.go
148 lines (116 loc) · 2.84 KB
/
filesystem.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
138
139
140
141
142
143
144
145
146
147
148
//Package fs provides filesystem based Stages for Gonzo.
package fs
import (
"bytes"
"errors"
"io/ioutil"
"os"
"path/filepath"
"github.com/go-gonzo/fs/glob"
"github.com/omeid/gonzo"
"github.com/omeid/gonzo/context"
)
// ErrIsDir is returned when a Directory is based where a file is expected.
var ErrIsDir = errors.New("path is a directory")
// Read is a simple helper function that opens the file from the given path and
// returns a pointer to a gonzo.File or an error.
func Read(path string) (gonzo.File, error) {
Stat, err := os.Stat(path)
if err != nil {
return nil, err
}
if Stat.IsDir() {
return nil, ErrIsDir
}
f, err := os.Open(path)
if err != nil {
return nil, err
}
return gonzo.NewFile(f, gonzo.FileInfoFrom(Stat)), nil
}
//Src returns a channel of gonzo.Files that match the provided patterns.
//TODO: ADD support for prefix to avoid all the util.Trims
func Src(ctx context.Context, globs ...string) gonzo.Pipe {
ctx, cancel := context.WithCancel(ctx)
files := make(chan gonzo.File)
pipe := gonzo.NewPipe(ctx, files)
//TODO: Parse globs here, check for invalid globs, split them into "filters".
go func() {
var err error
defer close(files)
fileslist, err := glob.Glob(globs...)
if err != nil {
ctx.Error(err)
return
}
for mp := range fileslist {
var (
file gonzo.File
base = glob.Dir(mp.Glob)
name = mp.Name
)
file, err = Read(mp.Name)
ctx = context.WithValue(ctx, "file", name)
if err == ErrIsDir {
ctx.Warn("fs.Src Ignored Directory.")
continue
}
if err != nil {
cancel()
ctx.Error(err)
return
}
file.FileInfo().SetBase(base)
file.FileInfo().SetName(name)
files <- file
}
}()
return pipe
}
// Dest writes the files from the input channel to the dst folder and closes the files.
// It never returns Files.
func Dest(dst string) gonzo.Stage {
return func(ctx context.Context, files <-chan gonzo.File, out chan<- gonzo.File) error {
for {
select {
case file, ok := <-files:
if !ok {
return nil
}
name := file.FileInfo().Name()
path := filepath.Join(dst, filepath.Dir(name))
err := os.MkdirAll(path, 0700)
if err != nil {
return err
}
if file.FileInfo().IsDir() {
out <- file
continue
}
content, err := ioutil.ReadAll(file)
if err != nil {
file.Close()
return err
}
ctx = context.WithValue(ctx, "path", path)
ctx.Infof("Writing %s", name)
err = writeFile(filepath.Join(dst, name), content)
if err != nil {
return err
}
out <- gonzo.NewFile(ioutil.NopCloser(bytes.NewReader(content)), file.FileInfo())
case <-ctx.Done():
return ctx.Err()
}
}
}
}
func writeFile(path string, content []byte) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(content)
return err
}