-
Notifications
You must be signed in to change notification settings - Fork 7
/
sequence.go
162 lines (146 loc) · 4.25 KB
/
sequence.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
package resources
import (
"io"
"path/filepath"
)
// BundleSequences are meta-bundles which contain a slice
// of sub-bundles to test/access sequentially for resources.
//
// Nil bundles are skipped, instead of causing errors or panics.
type BundleSequence []Bundle
// Close() is a no-op for BundleSequences; you must close
// the sub-bundles yourself.
func (bs BundleSequence) Close() error {
return nil
}
// Open finds the first sub-bundle where Open() doesn't return
// a ErrNotFound, and returns the io.ReadCloser.
//
// If any error other than ErrNotFound is seen, it is returned immediately.
func (bs BundleSequence) Open(path string) (io.ReadCloser, error) {
for _, bundle := range bs {
if bundle == nil {
continue
}
reader, err := bundle.Open(path)
if err == nil {
return reader, nil
} else if err != ErrNotFound {
return nil, err
}
}
return nil, ErrNotFound
}
// Find finds the first resource matching path in the sub-bundles.
// If multiple sub-bundles contain a resource a the given path, the
// resource from the earliest bundle is used.
//
// If any error other than ErrNotFound is seen, it is returned.
func (bs BundleSequence) Find(path string) (Resource, error) {
for _, bundle := range bs {
if bundle == nil {
continue
}
if searchable, ok := bundle.(Searcher); ok {
resource, err := searchable.Find(path)
if err == nil {
return resource, nil
} else if err != ErrNotFound {
return nil, err
}
}
}
return nil, ErrNotFound
}
// merge_resources merges two lists of resources and returns
// the resulting list. This operation is analgeous to append
// but doesn't append an element from extra into source if
// an element in source already exists with the same path.
func merge_resources(source, extra []Resource) []Resource {
for _, e := range extra {
found := false
for _, s := range source {
if s.Path() == e.Path() {
found = true
break
}
}
if !found {
source = append(source, e)
}
}
return source
}
// Glob finds the collection of all resources in all the sub-bundles
// which match the given glob pattern.
// In the event that multiple resources matched have the same path,
// the one from the earliest sub-bundle will be shown, all others
// will be suppressed.
func (bs BundleSequence) Glob(pattern string) (matches []Resource, err error) {
for _, bundle := range bs {
if bundle == nil {
continue
}
if searchable, ok := bundle.(Searcher); ok {
resources, err := searchable.Glob(pattern)
if err == nil {
matches = merge_resources(matches, resources)
} else if err != ErrNotFound {
return nil, err
}
}
}
return
}
// List provides a slice containing all resources from all the sub-bundles.
// Should multiple bundles contain a resource at the same path, only the
// first resource (from the first sub-bundle) will be present in the list.
func (bs BundleSequence) List() (resources []Resource, err error) {
for _, bundle := range bs {
if bundle == nil {
continue
}
if listable, ok := bundle.(Lister); ok {
list, err := listable.List()
if err != nil {
return nil, err
}
resources = merge_resources(resources, list)
}
}
return
}
// DefaultBundle represents a default search path of:
// - The current working directory
// - The directory containing the executable
// - The package source-code directory
// - The executable treated as a ZipBundle
var DefaultBundle BundleSequence
func init() {
var cwd, cur_pkg, exe_dir, exe Bundle
cwd = OpenFS(".")
cur_pkg = OpenAutoBundle(OpenCurrentPackage)
if exe_path, err := ExecutablePath(); err == nil {
exe_dir = OpenFS(filepath.Dir(exe_path))
if exe, err = OpenZip(exe_path); err == nil {
DefaultBundle = append(DefaultBundle, exe)
}
}
DefaultBundle = append(DefaultBundle, cwd, exe_dir, cur_pkg, exe)
}
// Open() is a shortcut for DefaultBundle.Open()
func Open(path string) (io.ReadCloser, error) {
return DefaultBundle.Open(path)
}
// Find() is a shortcut for DefaultBundle.Find()
func Find(path string) (Resource, error) {
return DefaultBundle.Find(path)
}
// Glob() is a shortcut for DefaultBundle.Glob()
func Glob(pattern string) ([]Resource, error) {
return DefaultBundle.Glob(pattern)
}
// List() is a shortcut for DefaultBundle.List()
func List() ([]Resource, error) {
return DefaultBundle.List()
}