/
asset.go
224 lines (191 loc) · 5.72 KB
/
asset.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
package statix
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/sarulabs/statix/helpers"
"github.com/sarulabs/statix/resource"
)
// Asset is the interface for assets managed by statix Manager.
type Asset interface {
RewritePaths(string, string) Asset
Dump([]Filter) error
}
// AssetPack implements the Asset interface. It includes all the assets
// located in the AssetPack.Input directory. Only the files with an output (without md5 suffix)
// matching the AssetPack.Pattern are part of the AssetPack.
// When the asset is dumped with the AssetPack.Dumper, AssetPack.Filters are applied before
// writing the assets in the AssetPack.Output directory.
type AssetPack struct {
Input string
Output string
Pattern Pattern
Alterations []resource.Alteration
Dumper Dumper
}
// RewritePaths returns a new AssetPack with updated input and output.
// More precisely, if the AssetPack.Input or AssetPack.Output is relative, it is prefixed
// by the `input` and `output` parameters.
func (ap AssetPack) RewritePaths(input, output string) Asset {
return AssetPack{
Input: helpers.RewritePath(input, ap.Input),
Output: helpers.RewritePath(output, ap.Output),
Pattern: ap.Pattern,
Alterations: ap.Alterations,
Dumper: FileDumper{},
}
}
// Dump reads files contained in AssetPack.Input and dumps
// them in AssetPack.Output if they match AssetPack.Pattern.
// If some filters are defined in AssetPack.Filters, they will be
// applied before dumping the assets with the AssetPack.Dumper.
// If some filters are passed in the `filters` parameter, they will be applied just after
// filters in AssetPack.Filters.
func (ap AssetPack) Dump(filters []Filter) error {
var r resource.Resource
files, err := ap.InputFiles()
if err != nil {
return err
}
for _, filename := range files {
c, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
r = resource.NewBytes(c)
for _, a := range ap.Alterations {
r, err = a.Alter(r)
if err != nil {
return err
}
}
output, err := ap.OutputFile(filename, "")
for _, f := range filters {
if f.Pattern.Match(output) {
r, err = f.Alteration.Alter(r)
if err != nil {
return err
}
}
}
c, err = r.Dump()
if err != nil {
return err
}
md5Output, err := ap.OutputFile(filename, "."+helpers.MD5(c))
if err != nil {
return err
}
err = ap.Dumper.Dump(md5Output, output, c)
if err != nil {
return err
}
}
return nil
}
// InputFiles returns all the files contained in AssetPack.Input
// that are not directories and that match AssetPack.Pattern.
func (ap AssetPack) InputFiles() ([]string, error) {
var walkError error
files := []string{}
info, err := os.Stat(ap.Input)
if err != nil || !info.IsDir() {
return files, fmt.Errorf("asset input `%s` is not a directory", ap.Input)
}
filepath.Walk(ap.Input, func(filename string, info os.FileInfo, err error) error {
if err != nil {
walkError = err
}
if info.IsDir() {
return nil
}
if ap.Pattern.Match(filename) {
files = append(files, filename)
}
return nil
})
return files, walkError
}
// OutputFile returns the absolute filename of an output based on the filename of the input.
// It also add a suffix in the basename (for example an md5 hash or a version number).
// The suffix is inserted just before the file extension and at the end of the filename
// if no extension was found.
func (ap AssetPack) OutputFile(filename string, suffix string) (string, error) {
filename, err := filepath.Abs(filename)
if err != nil {
return "", err
}
input, err := filepath.Abs(ap.Input)
if err != nil {
return "", err
}
if !strings.HasPrefix(filename, input) {
return "", errors.New(ap.Input + " is not a prefix of " + filename)
}
out := bytes.NewBuffer(nil)
out.WriteString(ap.Output)
out.WriteByte(os.PathSeparator)
out.WriteString(filename[len(input):])
return helpers.AddFileSuffix(out.String(), suffix), nil
}
// SingleAsset implements the Asset interface.
// It includes only one asset (SingleAsset.Input) implementing the Asset
// interface located in the asset package. The asset will be dump in the SingleAsset.Output file.
type SingleAsset struct {
Input resource.Resource
Output string
Dumper Dumper
}
// RewritePaths returns a new SingleAsset with updated input and output.
// More precisely, if the SingleAsset.Input or SingleAsset.Output path is relative, it is prefixed
// by the `input` and `output` parameters.
func (sa SingleAsset) RewritePaths(input, output string) Asset {
return SingleAsset{
Input: sa.Input.In(input),
Output: helpers.RewritePath(output, sa.Output),
Dumper: FileDumper{},
}
}
// Dump dumps the asset defined in SingleAsset.Input.
// If some filters are passed in the `filters` parameter, they will be applied before
// dumping the asset.
func (sa SingleAsset) Dump(filters []Filter) error {
r := sa.Input
output, err := sa.OutputFile("")
for _, f := range filters {
if f.Pattern.Match(output) {
r, err = f.Alteration.Alter(r)
if err != nil {
return err
}
}
}
c, err := r.Dump()
if err != nil {
return err
}
md5Output, err := sa.OutputFile("." + helpers.MD5(c))
if err != nil {
return err
}
err = sa.Dumper.Dump(md5Output, output, c)
if err != nil {
return err
}
return nil
}
// OutputFile returns the absolute filename of the SingleAsset.Output.
// It also add a suffix in the basename (for example an md5 hash or a version number).
// The suffix is inserted just before the file extension and at the end of the filename
// if no extension was found.
func (sa SingleAsset) OutputFile(suffix string) (string, error) {
out, err := filepath.Abs(sa.Output)
if err != nil {
return "", err
}
return helpers.AddFileSuffix(out, suffix), nil
}