@@ -9,13 +9,13 @@ package localdatasource
9
9
10
10
import (
11
11
"context"
12
+ "errors"
12
13
"fmt"
13
14
"os"
14
15
"path"
15
16
"path/filepath"
16
17
"strings"
17
18
"sync"
18
- "time"
19
19
20
20
"golang.org/x/pkgsite/internal"
21
21
"golang.org/x/pkgsite/internal/derrors"
@@ -29,66 +29,97 @@ type DataSource struct {
29
29
sourceClient * source.Client
30
30
31
31
mu sync.Mutex
32
+ getters []fetch.ModuleGetter
32
33
loadedModules map [string ]* internal.Module
33
34
}
34
35
35
36
// New creates and returns a new local datasource that bypasses license
36
37
// checks by default.
37
- func New () * DataSource {
38
+ func New (sc * source. Client ) * DataSource {
38
39
return & DataSource {
39
- sourceClient : source . NewClient ( 1 * time . Minute ) ,
40
+ sourceClient : sc ,
40
41
loadedModules : make (map [string ]* internal.Module ),
41
42
}
42
43
}
43
44
44
- // Load loads a module from the given local path. Loading is required before
45
- // being able to display the module.
46
- func (ds * DataSource ) Load (ctx context.Context , localPath string ) (err error ) {
47
- defer derrors .Wrap (& err , "Load(%q)" , localPath )
48
- return ds .fetch (ctx , "" , localPath )
45
+ // AddModuleGetter adds a module getter to the DataSource. To look up a module,
46
+ // the getters are tried in the order they were added until the desired module
47
+ // is found.
48
+ func (ds * DataSource ) AddModuleGetter (g fetch.ModuleGetter ) {
49
+ ds .mu .Lock ()
50
+ defer ds .mu .Unlock ()
51
+ ds .getters = append (ds .getters , g )
49
52
}
50
53
51
- // LoadFromGOPATH loads a module from GOPATH using the given import path. The full
52
- // path of the module should be GOPATH/src/importPath. If several GOPATHs exist, the
53
- // module is loaded from the first one that contains the import path. Loading is required
54
- // before being able to display the module.
55
- func (ds * DataSource ) LoadFromGOPATH (ctx context.Context , importPath string ) (err error ) {
56
- defer derrors .Wrap (& err , "LoadFromGOPATH(%q)" , importPath )
57
-
58
- path := getFullPath (importPath )
59
- if path == "" {
60
- return fmt .Errorf ("path %s doesn't exist: %w" , importPath , derrors .NotFound )
54
+ // getModule gets the module at the given path and version. It first checks the
55
+ // cache, and if it isn't there it then tries to fetch it.
56
+ func (ds * DataSource ) getModule (ctx context.Context , path , version string ) (* internal.Module , error ) {
57
+ if m := ds .getFromCache (path , version ); m != nil {
58
+ return m , nil
61
59
}
60
+ m , err := ds .fetch (ctx , path , version )
61
+ if err != nil {
62
+ return nil , err
63
+ }
64
+ ds .mu .Lock ()
65
+ defer ds .mu .Unlock ()
66
+ ds .loadedModules [m .ModulePath + "@" + m .Version ] = m
67
+ return m , nil
68
+ }
62
69
63
- return ds .fetch (ctx , importPath , path )
70
+ // getFromCache returns a module from the cache if it is present, and nil otherwise.
71
+ func (ds * DataSource ) getFromCache (path , version string ) * internal.Module {
72
+ ds .mu .Lock ()
73
+ defer ds .mu .Unlock ()
74
+ // Look for an exact match first.
75
+ if m := ds .loadedModules [path + "@" + version ]; m != nil {
76
+ return m
77
+ }
78
+ // Look for the module path with LocalVersion, as for a directory-based or GOPATH-mode module.
79
+ return ds .loadedModules [path + "@" + fetch .LocalVersion ]
64
80
}
65
81
66
- // fetch fetches a module using FetchLocalModule and adds it to the datasource.
67
- // If the fetching fails, an error is returned.
68
- func (ds * DataSource ) fetch (ctx context.Context , modulePath , localPath string ) error {
69
- fr := fetch .FetchLocalModule (ctx , modulePath , localPath , ds .sourceClient )
70
- if fr .Error != nil {
71
- return fr .Error
82
+ // fetch fetches a module using the configured ModuleGetters.
83
+ // It tries each getter in turn until it finds one that has the module.
84
+ func (ds * DataSource ) fetch (ctx context.Context , modulePath , version string ) (* internal.Module , error ) {
85
+ for _ , g := range ds .getters {
86
+ fr := fetch .FetchModule (ctx , modulePath , version , g , ds .sourceClient )
87
+ if fr .Error == nil {
88
+ adjust (fr .Module )
89
+ return fr .Module , nil
90
+ }
91
+ if ! errors .Is (fr .Error , derrors .NotFound ) {
92
+ return nil , fr .Error
93
+ }
72
94
}
95
+ return nil , fmt .Errorf ("%s@%s: %w" , modulePath , version , derrors .NotFound )
96
+ }
73
97
74
- fr .Module .IsRedistributable = true
75
- for _ , unit := range fr .Module .Units {
98
+ func adjust (m * internal.Module ) {
99
+ m .IsRedistributable = true
100
+ for _ , unit := range m .Units {
76
101
unit .IsRedistributable = true
77
102
}
78
-
79
- for _ , unit := range fr .Module .Units {
103
+ for _ , unit := range m .Units {
80
104
for _ , d := range unit .Documentation {
81
105
unit .BuildContexts = append (unit .BuildContexts , internal.BuildContext {
82
106
GOOS : d .GOOS ,
83
107
GOARCH : d .GOARCH ,
84
108
})
85
109
}
86
110
}
111
+ }
87
112
88
- ds .mu .Lock ()
89
- defer ds .mu .Unlock ()
90
- ds .loadedModules [fr .ModulePath ] = fr .Module
91
- return nil
113
+ // NewGOPATHModuleGetter returns a module getter that uses the GOPATH
114
+ // environment variable to find the module with the given import path.
115
+ func NewGOPATHModuleGetter (importPath string ) (_ fetch.ModuleGetter , err error ) {
116
+ defer derrors .Wrap (& err , "NewGOPATHModuleGetter(%q)" , importPath )
117
+
118
+ dir := getFullPath (importPath )
119
+ if dir == "" {
120
+ return nil , fmt .Errorf ("path %s doesn't exist: %w" , importPath , derrors .NotFound )
121
+ }
122
+ return fetch .NewDirectoryModuleGetter (importPath , dir )
92
123
}
93
124
94
125
// getFullPath takes an import path, tests it relative to each GOPATH, and returns
@@ -111,51 +142,30 @@ func getFullPath(modulePath string) string {
111
142
func (ds * DataSource ) GetUnit (ctx context.Context , pathInfo * internal.UnitMeta , fields internal.FieldSet , bc internal.BuildContext ) (_ * internal.Unit , err error ) {
112
143
defer derrors .Wrap (& err , "GetUnit(%q, %q)" , pathInfo .Path , pathInfo .ModulePath )
113
144
114
- modulepath := pathInfo .ModulePath
115
- path := pathInfo .Path
116
-
117
- ds .mu .Lock ()
118
- defer ds .mu .Unlock ()
119
- if ds .loadedModules [modulepath ] == nil {
120
- return nil , fmt .Errorf ("%s not loaded: %w" , modulepath , derrors .NotFound )
145
+ module , err := ds .getModule (ctx , pathInfo .ModulePath , pathInfo .Version )
146
+ if err != nil {
147
+ return nil , err
121
148
}
122
-
123
- module := ds .loadedModules [modulepath ]
124
149
for _ , unit := range module .Units {
125
- if unit .Path == path {
150
+ if unit .Path == pathInfo . Path {
126
151
return unit , nil
127
152
}
128
153
}
129
154
130
- return nil , fmt .Errorf ("%s not found: %w" , path , derrors .NotFound )
155
+ return nil , fmt .Errorf ("import path %s not found in module %s : %w" , pathInfo . Path , pathInfo . ModulePath , derrors .NotFound )
131
156
}
132
157
133
158
// GetUnitMeta returns information about a path.
134
159
func (ds * DataSource ) GetUnitMeta (ctx context.Context , path , requestedModulePath , requestedVersion string ) (_ * internal.UnitMeta , err error ) {
135
160
defer derrors .Wrap (& err , "GetUnitMeta(%q, %q, %q)" , path , requestedModulePath , requestedVersion )
136
161
137
- if requestedModulePath == internal .UnknownModulePath {
138
- requestedModulePath , err = ds .findModule (path )
139
- if err != nil {
140
- return nil , err
141
- }
142
- }
143
-
144
- ds .mu .Lock ()
145
- module := ds .loadedModules [requestedModulePath ]
146
- ds .mu .Unlock ()
147
- if module == nil {
148
- return nil , fmt .Errorf ("%s not loaded: %w" , requestedModulePath , derrors .NotFound )
162
+ module , err := ds .findModule (ctx , path , requestedModulePath , requestedVersion )
163
+ if err != nil {
164
+ return nil , err
149
165
}
150
-
151
166
um := & internal.UnitMeta {
152
- Path : path ,
153
- ModuleInfo : internal.ModuleInfo {
154
- ModulePath : requestedModulePath ,
155
- Version : fetch .LocalVersion ,
156
- CommitTime : fetch .LocalCommitTime ,
157
- IsRedistributable : module .IsRedistributable ,
158
- },
167
+ Path : path ,
168
+ ModuleInfo : module .ModuleInfo ,
159
169
}
160
170
161
171
for _ , u := range module .Units {
@@ -168,23 +178,26 @@ func (ds *DataSource) GetUnitMeta(ctx context.Context, path, requestedModulePath
168
178
return um , nil
169
179
}
170
180
171
- // findModule finds the longest module path in loadedModules containing the given
172
- // package path. It iteratively checks parent directories to find an import path.
173
- // Returns an error if no module is found.
174
- func (ds * DataSource ) findModule (pkgPath string ) (_ string , err error ) {
181
+ // findModule finds the module with longest module path containing the given
182
+ // package path. It returns an error if no module is found.
183
+ func (ds * DataSource ) findModule (ctx context.Context , pkgPath , modulePath , version string ) (_ * internal.Module , err error ) {
175
184
defer derrors .Wrap (& err , "findModule(%q)" , pkgPath )
176
185
177
- pkgPath = strings .TrimLeft (pkgPath , "/" )
186
+ if modulePath != internal .UnknownModulePath {
187
+ return ds .getModule (ctx , modulePath , version )
188
+ }
178
189
179
- ds .mu .Lock ()
180
- defer ds .mu .Unlock ()
190
+ pkgPath = strings .TrimLeft (pkgPath , "/" )
181
191
for modulePath := pkgPath ; modulePath != "" && modulePath != "." ; modulePath = path .Dir (modulePath ) {
182
- if ds .loadedModules [modulePath ] != nil {
183
- return modulePath , nil
192
+ m , err := ds .getModule (ctx , modulePath , version )
193
+ if err == nil {
194
+ return m , nil
195
+ }
196
+ if ! errors .Is (err , derrors .NotFound ) {
197
+ return nil , err
184
198
}
185
199
}
186
-
187
- return "" , fmt .Errorf ("%s not loaded: %w" , pkgPath , derrors .NotFound )
200
+ return nil , fmt .Errorf ("could not find module for import path %s: %w" , pkgPath , derrors .NotFound )
188
201
}
189
202
190
203
// GetLatestInfo is not implemented.
0 commit comments