-
Notifications
You must be signed in to change notification settings - Fork 0
/
file.go
121 lines (103 loc) · 2.22 KB
/
file.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
package main
import (
"compress/bzip2"
"compress/gzip"
"io"
"io/ioutil"
"math"
"path/filepath"
"strings"
"time"
"github.com/ulikunitz/xz"
)
type file interface {
name() string
size() (uint64, error)
modTime() time.Time
readCloser() (io.ReadCloser, error)
}
func allBytes(f file) ([]byte, error) {
rc, err := f.readCloser()
if err != nil {
return nil, err
}
defer rc.Close()
return ioutil.ReadAll(rc)
}
func newGzipFile(f file) *compressedFile {
d := func(r io.Reader) (io.Reader, error) { return gzip.NewReader(r) }
return &compressedFile{f, d, math.MaxUint64}
}
func newXzFile(f file) *compressedFile {
d := func(r io.Reader) (io.Reader, error) { return xz.NewReader(r) }
return &compressedFile{f, d, math.MaxUint64}
}
func newBzip2File(f file) *compressedFile {
d := func(r io.Reader) (io.Reader, error) { return bzip2.NewReader(r), nil }
return &compressedFile{f, d, math.MaxUint64}
}
func removeExt(path string) string {
return strings.TrimSuffix(path, filepath.Ext(path))
}
func isCompressed(path string) bool {
pred := func(ext string) bool { return strings.HasSuffix(path, ext) }
return pred(".gz") || pred(".xz") || pred(".bz2")
}
func uncompressedName(path string) string {
if isCompressed(path) {
return removeExt(path)
}
return path
}
func newFile(f file, ext string) file {
switch ext {
case ".gz":
return newGzipFile(f)
case ".xz":
return newXzFile(f)
case ".bz2":
return newBzip2File(f)
default:
return f
}
}
type compressedFile struct {
file
decompressor func(io.Reader) (io.Reader, error)
s uint64
}
func (f *compressedFile) name() string {
return f.file.name()
}
type readcloser struct {
io.Reader
close func() error
}
func (r *readcloser) Close() error {
return r.close()
}
func (f *compressedFile) readCloser() (io.ReadCloser, error) {
r, err := f.file.readCloser()
if err != nil {
return nil, err
}
d, err := f.decompressor(r)
if err != nil {
r.Close()
return nil, err
}
return &readcloser{d, r.Close}, nil
}
func (f *compressedFile) size() (uint64, error) {
if f.s == math.MaxUint64 {
b, err := allBytes(f)
if err != nil {
return 0, err
}
f.s = uint64(len(b))
}
return f.s, nil
}
func (f *compressedFile) String() string {
return f.name()
}