/
fetch.go
131 lines (107 loc) · 2.93 KB
/
fetch.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
/* fetch.go
Package fetching interface and package fetching error interface.
*/
package main
import (
"fmt"
"os"
)
type FetchError interface {
// returns an error message, like os.Error
String() string
// returns true if no matching package name could be found
NotFound() bool
}
type FetchErrorRaw struct {
missing bool
query string
message string
}
type FetchErrorWrapper struct {
pkgname string
oserr os.Error
}
func (err *FetchErrorRaw) String() string {
if err.missing {
return fmt.Sprintf("target not found %s", err.query)
}
return err.message
}
func (err *FetchErrorRaw) NotFound() bool {
return err.missing
}
func NewFetchError(pkgname string, errmsg string) FetchError {
return &FetchErrorRaw{false, pkgname, errmsg}
}
// NotFoundError creates a FetchError for pkgname which returns true for NotFound()
func NotFoundError(pkgname string) FetchError {
return &FetchErrorRaw{true, pkgname, ""}
}
// FetchErrorWrap wraps an os.Error into a FetchError
func FetchErrorWrap(pkgname string, oserr os.Error) FetchError {
return &FetchErrorWrapper{pkgname, oserr}
}
func (wrap *FetchErrorWrapper) String() string {
return wrap.oserr.String()
}
func (wrap *FetchErrorWrapper) NotFound() bool {
return false
}
type PackageFetcher interface {
Fetch(pkgname string) ([]string, FetchError)
}
////////////////////////////////////////////////////////////////////////////////
// The MultiFetcher not only fetches from multiple other fetchers but also fetches multiple
// packages at the same time! Zing!
type MultiFetcher struct {
fetchers []PackageFetcher
}
type fetchResult struct {
pkgs []string
error FetchError
}
func NewMultiFetcher(fetchers ...PackageFetcher) *MultiFetcher {
return &MultiFetcher{fetchers}
}
func (mf *MultiFetcher) FetchAll(pkgnames []string) ([]string, os.Error) {
// Packages are all fetched concurrently, independent of each other
chans := make([]chan *fetchResult, len(pkgnames))
for i, pkgname := range pkgnames {
r := make(chan *fetchResult, 1)
go mf.chanFetch(pkgname, r)
chans[i] = r
}
// Waits for all goroutines to finish, collecting results
allpkgpaths := make([]string, 0, 256) // TODO: use cap or something?
for _, c := range chans {
result := <-c
if result.error == nil {
allpkgpaths = append(allpkgpaths, result.pkgs...)
} else {
return nil, result.error
}
}
return allpkgpaths, nil
}
// chanFetch is a simple wrapper to make Fetch more concurrent.
func (mf *MultiFetcher) chanFetch(pkgname string, results chan *fetchResult) {
paths, err := mf.Fetch(pkgname)
results <- &fetchResult{paths, err}
}
func (mf *MultiFetcher) Fetch(pkgname string) ([]string, FetchError) {
var pkgpaths []string
SearchLoop:
for _, fetcher := range mf.fetchers {
var err FetchError
pkgpaths, err = fetcher.Fetch(pkgname)
if pkgpaths != nil {
return pkgpaths, nil
} else {
if err.NotFound() {
continue SearchLoop
}
return nil, err
}
}
return nil, NotFoundError(pkgname)
}