1
2
3
4
5 package modload
6
7 import (
8 "context"
9 "errors"
10 "fmt"
11 "io/fs"
12 "os"
13 pathpkg "path"
14 "path/filepath"
15 "sort"
16 "strings"
17 "sync"
18 "time"
19
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/imports"
22 "cmd/go/internal/modfetch"
23 "cmd/go/internal/search"
24 "cmd/go/internal/str"
25 "cmd/go/internal/trace"
26
27 "golang.org/x/mod/module"
28 "golang.org/x/mod/semver"
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 func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
69 var info *modfetch.RevInfo
70 err := modfetch.TryProxies(func(proxy string) (err error) {
71 info, err = queryProxy(ctx, proxy, path, query, current, allowed)
72 return err
73 })
74 return info, err
75 }
76
77
78
79
80
81
82
83
84
85
86 type AllowedFunc func(context.Context, module.Version) error
87
88 var errQueryDisabled error = queryDisabledError{}
89
90 type queryDisabledError struct{}
91
92 func (queryDisabledError) Error() string {
93 if cfg.BuildModReason == "" {
94 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
95 }
96 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
97 }
98
99 func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
100 ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
101 defer span.Done()
102
103 if current != "" && current != "none" && !semver.IsValid(current) {
104 return nil, fmt.Errorf("invalid previous version %q", current)
105 }
106 if cfg.BuildMod == "vendor" {
107 return nil, errQueryDisabled
108 }
109 if allowed == nil {
110 allowed = func(context.Context, module.Version) error { return nil }
111 }
112
113 if path == Target.Path && (query == "upgrade" || query == "patch") {
114 if err := allowed(ctx, Target); err != nil {
115 return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
116 }
117 return &modfetch.RevInfo{Version: Target.Version}, nil
118 }
119
120 if path == "std" || path == "cmd" {
121 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
122 }
123
124 repo, err := lookupRepo(proxy, path)
125 if err != nil {
126 return nil, err
127 }
128
129
130
131 qm, err := newQueryMatcher(path, query, current, allowed)
132 if (err == nil && qm.canStat) || err == errRevQuery {
133
134
135
136
137
138
139 info, err := repo.Stat(query)
140 if err != nil {
141 queryErr := err
142
143
144
145 canonicalQuery := module.CanonicalVersion(query)
146 if canonicalQuery != "" && query != canonicalQuery {
147 info, err = repo.Stat(canonicalQuery)
148 if err != nil && !errors.Is(err, fs.ErrNotExist) {
149 return info, err
150 }
151 }
152 if err != nil {
153 return nil, queryErr
154 }
155 }
156 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
157 return nil, err
158 }
159 return info, nil
160 } else if err != nil {
161 return nil, err
162 }
163
164
165 versions, err := repo.Versions(qm.prefix)
166 if err != nil {
167 return nil, err
168 }
169 releases, prereleases, err := qm.filterVersions(ctx, versions)
170 if err != nil {
171 return nil, err
172 }
173
174 lookup := func(v string) (*modfetch.RevInfo, error) {
175 rev, err := repo.Stat(v)
176 if err != nil {
177 return nil, err
178 }
179
180 if (query == "upgrade" || query == "patch") && modfetch.IsPseudoVersion(current) && !rev.Time.IsZero() {
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 currentTime, err := modfetch.PseudoVersionTime(current)
200 if err == nil && rev.Time.Before(currentTime) {
201 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
202 return nil, err
203 }
204 return repo.Stat(current)
205 }
206 }
207
208 return rev, nil
209 }
210
211 if qm.preferLower {
212 if len(releases) > 0 {
213 return lookup(releases[0])
214 }
215 if len(prereleases) > 0 {
216 return lookup(prereleases[0])
217 }
218 } else {
219 if len(releases) > 0 {
220 return lookup(releases[len(releases)-1])
221 }
222 if len(prereleases) > 0 {
223 return lookup(prereleases[len(prereleases)-1])
224 }
225 }
226
227 if qm.mayUseLatest {
228 latest, err := repo.Latest()
229 if err == nil {
230 if qm.allowsVersion(ctx, latest.Version) {
231 return lookup(latest.Version)
232 }
233 } else if !errors.Is(err, fs.ErrNotExist) {
234 return nil, err
235 }
236 }
237
238 if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
239
240 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
241 return nil, err
242 }
243 return lookup(current)
244 }
245
246 return nil, &NoMatchingVersionError{query: query, current: current}
247 }
248
249
250
251
252
253 func IsRevisionQuery(vers string) bool {
254 if vers == "latest" ||
255 vers == "upgrade" ||
256 vers == "patch" ||
257 strings.HasPrefix(vers, "<") ||
258 strings.HasPrefix(vers, ">") ||
259 (semver.IsValid(vers) && isSemverPrefix(vers)) {
260 return false
261 }
262 return true
263 }
264
265
266
267 func isSemverPrefix(v string) bool {
268 dots := 0
269 for i := 0; i < len(v); i++ {
270 switch v[i] {
271 case '-', '+':
272 return false
273 case '.':
274 dots++
275 if dots >= 2 {
276 return false
277 }
278 }
279 }
280 return true
281 }
282
283 type queryMatcher struct {
284 path string
285 prefix string
286 filter func(version string) bool
287 allowed AllowedFunc
288 canStat bool
289 preferLower bool
290 mayUseLatest bool
291 preferIncompatible bool
292 }
293
294 var errRevQuery = errors.New("query refers to a non-semver revision")
295
296
297
298
299
300
301 func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
302 badVersion := func(v string) (*queryMatcher, error) {
303 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
304 }
305
306 matchesMajor := func(v string) bool {
307 _, pathMajor, ok := module.SplitPathVersion(path)
308 if !ok {
309 return false
310 }
311 return module.CheckPathMajor(v, pathMajor) == nil
312 }
313
314 qm := &queryMatcher{
315 path: path,
316 allowed: allowed,
317 preferIncompatible: strings.HasSuffix(current, "+incompatible"),
318 }
319
320 switch {
321 case query == "latest":
322 qm.mayUseLatest = true
323
324 case query == "upgrade":
325 if current == "" || current == "none" {
326 qm.mayUseLatest = true
327 } else {
328 qm.mayUseLatest = modfetch.IsPseudoVersion(current)
329 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
330 }
331
332 case query == "patch":
333 if current == "none" {
334 return nil, &NoPatchBaseError{path}
335 }
336 if current == "" {
337 qm.mayUseLatest = true
338 } else {
339 qm.mayUseLatest = modfetch.IsPseudoVersion(current)
340 qm.prefix = semver.MajorMinor(current) + "."
341 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
342 }
343
344 case strings.HasPrefix(query, "<="):
345 v := query[len("<="):]
346 if !semver.IsValid(v) {
347 return badVersion(v)
348 }
349 if isSemverPrefix(v) {
350
351 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
352 }
353 qm.filter = func(mv string) bool { return semver.Compare(mv, v) <= 0 }
354 if !matchesMajor(v) {
355 qm.preferIncompatible = true
356 }
357
358 case strings.HasPrefix(query, "<"):
359 v := query[len("<"):]
360 if !semver.IsValid(v) {
361 return badVersion(v)
362 }
363 qm.filter = func(mv string) bool { return semver.Compare(mv, v) < 0 }
364 if !matchesMajor(v) {
365 qm.preferIncompatible = true
366 }
367
368 case strings.HasPrefix(query, ">="):
369 v := query[len(">="):]
370 if !semver.IsValid(v) {
371 return badVersion(v)
372 }
373 qm.filter = func(mv string) bool { return semver.Compare(mv, v) >= 0 }
374 qm.preferLower = true
375 if !matchesMajor(v) {
376 qm.preferIncompatible = true
377 }
378
379 case strings.HasPrefix(query, ">"):
380 v := query[len(">"):]
381 if !semver.IsValid(v) {
382 return badVersion(v)
383 }
384 if isSemverPrefix(v) {
385
386 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
387 }
388 qm.filter = func(mv string) bool { return semver.Compare(mv, v) > 0 }
389 qm.preferLower = true
390 if !matchesMajor(v) {
391 qm.preferIncompatible = true
392 }
393
394 case semver.IsValid(query):
395 if isSemverPrefix(query) {
396 qm.prefix = query + "."
397
398
399 qm.filter = func(mv string) bool { return semver.Compare(mv, query) >= 0 }
400 } else {
401 qm.canStat = true
402 qm.filter = func(mv string) bool { return semver.Compare(mv, query) == 0 }
403 qm.prefix = semver.Canonical(query)
404 }
405 if !matchesMajor(query) {
406 qm.preferIncompatible = true
407 }
408
409 default:
410 return nil, errRevQuery
411 }
412
413 return qm, nil
414 }
415
416
417
418 func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
419 if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
420 return false
421 }
422 if qm.filter != nil && !qm.filter(v) {
423 return false
424 }
425 if qm.allowed != nil {
426 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
427 return false
428 }
429 }
430 return true
431 }
432
433
434
435
436
437
438
439
440
441 func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
442 needIncompatible := qm.preferIncompatible
443
444 var lastCompatible string
445 for _, v := range versions {
446 if !qm.allowsVersion(ctx, v) {
447 continue
448 }
449
450 if !needIncompatible {
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 if !strings.HasSuffix(v, "+incompatible") {
468 lastCompatible = v
469 } else if lastCompatible != "" {
470
471
472
473
474 ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
475 if err != nil {
476 return nil, nil, err
477 }
478 if ok {
479
480
481
482
483 break
484 }
485
486
487
488
489 needIncompatible = true
490 }
491 }
492
493 if semver.Prerelease(v) != "" {
494 prereleases = append(prereleases, v)
495 } else {
496 releases = append(releases, v)
497 }
498 }
499
500 return releases, prereleases, nil
501 }
502
503 type QueryResult struct {
504 Mod module.Version
505 Rev *modfetch.RevInfo
506 Packages []string
507 }
508
509
510
511 func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
512 pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
513
514 if len(pkgMods) == 0 && err == nil {
515 return nil, &PackageNotInModuleError{
516 Mod: modOnly.Mod,
517 Replacement: Replacement(modOnly.Mod),
518 Query: query,
519 Pattern: pattern,
520 }
521 }
522
523 return pkgMods, err
524 }
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541 func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
542 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
543 defer span.Done()
544
545 base := pattern
546
547 firstError := func(m *search.Match) error {
548 if len(m.Errs) == 0 {
549 return nil
550 }
551 return m.Errs[0]
552 }
553
554 var match func(mod module.Version, root string, isLocal bool) *search.Match
555 matchPattern := search.MatchPattern(pattern)
556
557 if i := strings.Index(pattern, "..."); i >= 0 {
558 base = pathpkg.Dir(pattern[:i+3])
559 if base == "." {
560 return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
561 }
562 match = func(mod module.Version, root string, isLocal bool) *search.Match {
563 m := search.NewMatch(pattern)
564 matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
565 return m
566 }
567 } else {
568 match = func(mod module.Version, root string, isLocal bool) *search.Match {
569 m := search.NewMatch(pattern)
570 prefix := mod.Path
571 if mod == Target {
572 prefix = targetPrefix
573 }
574 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
575 m.AddError(err)
576 } else if ok {
577 m.Pkgs = []string{pattern}
578 }
579 return m
580 }
581 }
582
583 var queryMatchesMainModule bool
584 if HasModRoot() {
585 m := match(Target, modRoot, true)
586 if len(m.Pkgs) > 0 {
587 if query != "upgrade" && query != "patch" {
588 return nil, nil, &QueryMatchesPackagesInMainModuleError{
589 Pattern: pattern,
590 Query: query,
591 Packages: m.Pkgs,
592 }
593 }
594 if err := allowed(ctx, Target); err != nil {
595 return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, Target.Path, err)
596 }
597 return []QueryResult{{
598 Mod: Target,
599 Rev: &modfetch.RevInfo{Version: Target.Version},
600 Packages: m.Pkgs,
601 }}, nil, nil
602 }
603 if err := firstError(m); err != nil {
604 return nil, nil, err
605 }
606
607 if matchPattern(Target.Path) {
608 queryMatchesMainModule = true
609 }
610
611 if (query == "upgrade" || query == "patch") && queryMatchesMainModule {
612 if err := allowed(ctx, Target); err == nil {
613 modOnly = &QueryResult{
614 Mod: Target,
615 Rev: &modfetch.RevInfo{Version: Target.Version},
616 }
617 }
618 }
619 }
620
621 var (
622 results []QueryResult
623 candidateModules = modulePrefixesExcludingTarget(base)
624 )
625 if len(candidateModules) == 0 {
626 if modOnly != nil {
627 return nil, modOnly, nil
628 } else if queryMatchesMainModule {
629 return nil, nil, &QueryMatchesMainModuleError{
630 Pattern: pattern,
631 Query: query,
632 }
633 } else {
634 return nil, nil, &PackageNotInModuleError{
635 Mod: Target,
636 Query: query,
637 Pattern: pattern,
638 }
639 }
640 }
641
642 err = modfetch.TryProxies(func(proxy string) error {
643 queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
644 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
645 defer span.Done()
646
647 pathCurrent := current(path)
648 r.Mod.Path = path
649 r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed)
650 if err != nil {
651 return r, err
652 }
653 r.Mod.Version = r.Rev.Version
654 needSum := true
655 root, isLocal, err := fetch(ctx, r.Mod, needSum)
656 if err != nil {
657 return r, err
658 }
659 m := match(r.Mod, root, isLocal)
660 r.Packages = m.Pkgs
661 if len(r.Packages) == 0 && !matchPattern(path) {
662 if err := firstError(m); err != nil {
663 return r, err
664 }
665 return r, &PackageNotInModuleError{
666 Mod: r.Mod,
667 Replacement: Replacement(r.Mod),
668 Query: query,
669 Pattern: pattern,
670 }
671 }
672 return r, nil
673 }
674
675 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
676 results = allResults[:0]
677 for _, r := range allResults {
678 if len(r.Packages) == 0 {
679 modOnly = &r
680 } else {
681 results = append(results, r)
682 }
683 }
684 return err
685 })
686
687 if queryMatchesMainModule && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
688 return nil, nil, &QueryMatchesMainModuleError{
689 Pattern: pattern,
690 Query: query,
691 }
692 }
693 return results[:len(results):len(results)], modOnly, err
694 }
695
696
697
698
699 func modulePrefixesExcludingTarget(path string) []string {
700 prefixes := make([]string, 0, strings.Count(path, "/")+1)
701
702 for {
703 if path != targetPrefix {
704 if _, _, ok := module.SplitPathVersion(path); ok {
705 prefixes = append(prefixes, path)
706 }
707 }
708
709 j := strings.LastIndexByte(path, '/')
710 if j < 0 {
711 break
712 }
713 path = path[:j]
714 }
715
716 return prefixes
717 }
718
719 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
720 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
721 defer span.Done()
722
723
724
725
726
727 type result struct {
728 QueryResult
729 err error
730 }
731 results := make([]result, len(candidateModules))
732 var wg sync.WaitGroup
733 wg.Add(len(candidateModules))
734 for i, p := range candidateModules {
735 ctx := trace.StartGoroutine(ctx)
736 go func(p string, r *result) {
737 r.QueryResult, r.err = queryModule(ctx, p)
738 wg.Done()
739 }(p, &results[i])
740 }
741 wg.Wait()
742
743
744
745
746 var (
747 noPackage *PackageNotInModuleError
748 noVersion *NoMatchingVersionError
749 noPatchBase *NoPatchBaseError
750 notExistErr error
751 )
752 for _, r := range results {
753 switch rErr := r.err.(type) {
754 case nil:
755 found = append(found, r.QueryResult)
756 case *PackageNotInModuleError:
757
758
759 if noPackage == nil || noPackage.Mod == Target {
760 noPackage = rErr
761 }
762 case *NoMatchingVersionError:
763 if noVersion == nil {
764 noVersion = rErr
765 }
766 case *NoPatchBaseError:
767 if noPatchBase == nil {
768 noPatchBase = rErr
769 }
770 default:
771 if errors.Is(rErr, fs.ErrNotExist) {
772 if notExistErr == nil {
773 notExistErr = rErr
774 }
775 } else if err == nil {
776 if len(found) > 0 || noPackage != nil {
777
778
779
780
781
782
783
784 } else {
785 err = r.err
786 }
787 }
788 }
789 }
790
791
792
793
794
795 if len(found) == 0 && err == nil {
796 switch {
797 case noPackage != nil:
798 err = noPackage
799 case noVersion != nil:
800 err = noVersion
801 case noPatchBase != nil:
802 err = noPatchBase
803 case notExistErr != nil:
804 err = notExistErr
805 default:
806 panic("queryPrefixModules: no modules found, but no error detected")
807 }
808 }
809
810 return found, err
811 }
812
813
814
815
816
817
818
819
820
821 type NoMatchingVersionError struct {
822 query, current string
823 }
824
825 func (e *NoMatchingVersionError) Error() string {
826 currentSuffix := ""
827 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
828 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
829 }
830 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
831 }
832
833
834
835 type NoPatchBaseError struct {
836 path string
837 }
838
839 func (e *NoPatchBaseError) Error() string {
840 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
841 }
842
843
844
845
846 type WildcardInFirstElementError struct {
847 Pattern string
848 Query string
849 }
850
851 func (e *WildcardInFirstElementError) Error() string {
852 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
853 }
854
855
856
857
858
859
860
861
862
863
864 type PackageNotInModuleError struct {
865 Mod module.Version
866 Replacement module.Version
867 Query string
868 Pattern string
869 }
870
871 func (e *PackageNotInModuleError) Error() string {
872 if e.Mod == Target {
873 if strings.Contains(e.Pattern, "...") {
874 return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern)
875 }
876 return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern)
877 }
878
879 found := ""
880 if r := e.Replacement; r.Path != "" {
881 replacement := r.Path
882 if r.Version != "" {
883 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
884 }
885 if e.Query == e.Mod.Version {
886 found = fmt.Sprintf(" (replaced by %s)", replacement)
887 } else {
888 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
889 }
890 } else if e.Query != e.Mod.Version {
891 found = fmt.Sprintf(" (%s)", e.Mod.Version)
892 }
893
894 if strings.Contains(e.Pattern, "...") {
895 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
896 }
897 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
898 }
899
900 func (e *PackageNotInModuleError) ImportPath() string {
901 if !strings.Contains(e.Pattern, "...") {
902 return e.Pattern
903 }
904 return ""
905 }
906
907
908 func ModuleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
909 needSum := false
910 root, isLocal, err := fetch(ctx, m, needSum)
911 if err != nil {
912 return false, err
913 }
914 _, ok, err := dirInModule(m.Path, m.Path, root, isLocal)
915 return ok, err
916 }
917
918 func versionHasGoMod(ctx context.Context, m module.Version) (bool, error) {
919 needSum := false
920 root, _, err := fetch(ctx, m, needSum)
921 if err != nil {
922 return false, err
923 }
924 fi, err := os.Stat(filepath.Join(root, "go.mod"))
925 return err == nil && !fi.IsDir(), nil
926 }
927
928
929
930 type versionRepo interface {
931 ModulePath() string
932 Versions(prefix string) ([]string, error)
933 Stat(rev string) (*modfetch.RevInfo, error)
934 Latest() (*modfetch.RevInfo, error)
935 }
936
937 var _ versionRepo = modfetch.Repo(nil)
938
939 func lookupRepo(proxy, path string) (repo versionRepo, err error) {
940 err = module.CheckPath(path)
941 if err == nil {
942 repo = modfetch.Lookup(proxy, path)
943 } else {
944 repo = emptyRepo{path: path, err: err}
945 }
946
947 if index == nil {
948 return repo, err
949 }
950 if _, ok := index.highestReplaced[path]; !ok {
951 return repo, err
952 }
953
954 return &replacementRepo{repo: repo}, nil
955 }
956
957
958 type emptyRepo struct {
959 path string
960 err error
961 }
962
963 var _ versionRepo = emptyRepo{}
964
965 func (er emptyRepo) ModulePath() string { return er.path }
966 func (er emptyRepo) Versions(prefix string) ([]string, error) { return nil, nil }
967 func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err }
968 func (er emptyRepo) Latest() (*modfetch.RevInfo, error) { return nil, er.err }
969
970
971
972
973
974
975
976 type replacementRepo struct {
977 repo versionRepo
978 }
979
980 var _ versionRepo = (*replacementRepo)(nil)
981
982 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
983
984
985
986 func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
987 repoVersions, err := rr.repo.Versions(prefix)
988 if err != nil && !errors.Is(err, os.ErrNotExist) {
989 return nil, err
990 }
991
992 versions := repoVersions
993 if index != nil && len(index.replace) > 0 {
994 path := rr.ModulePath()
995 for m, _ := range index.replace {
996 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !modfetch.IsPseudoVersion(m.Version) {
997 versions = append(versions, m.Version)
998 }
999 }
1000 }
1001
1002 if len(versions) == len(repoVersions) {
1003 return versions, nil
1004 }
1005
1006 sort.Slice(versions, func(i, j int) bool {
1007 return semver.Compare(versions[i], versions[j]) < 0
1008 })
1009 str.Uniq(&versions)
1010 return versions, nil
1011 }
1012
1013 func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
1014 info, err := rr.repo.Stat(rev)
1015 if err == nil || index == nil || len(index.replace) == 0 {
1016 return info, err
1017 }
1018
1019 v := module.CanonicalVersion(rev)
1020 if v != rev {
1021
1022
1023 return info, err
1024 }
1025
1026 path := rr.ModulePath()
1027 _, pathMajor, ok := module.SplitPathVersion(path)
1028 if ok && pathMajor == "" {
1029 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
1030 v += "+incompatible"
1031 }
1032 }
1033
1034 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1035 return info, err
1036 }
1037 return rr.replacementStat(v)
1038 }
1039
1040 func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
1041 info, err := rr.repo.Latest()
1042
1043 if index != nil {
1044 path := rr.ModulePath()
1045 if v, ok := index.highestReplaced[path]; ok {
1046 if v == "" {
1047
1048
1049
1050
1051
1052 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
1053 v = modfetch.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
1054 } else {
1055 v = modfetch.PseudoVersion("v0", "", time.Time{}, "000000000000")
1056 }
1057 }
1058
1059 if err != nil || semver.Compare(v, info.Version) > 0 {
1060 return rr.replacementStat(v)
1061 }
1062 }
1063 }
1064
1065 return info, err
1066 }
1067
1068 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
1069 rev := &modfetch.RevInfo{Version: v}
1070 if modfetch.IsPseudoVersion(v) {
1071 rev.Time, _ = modfetch.PseudoVersionTime(v)
1072 rev.Short, _ = modfetch.PseudoVersionRev(v)
1073 }
1074 return rev, nil
1075 }
1076
1077
1078
1079
1080 type QueryMatchesMainModuleError struct {
1081 Pattern string
1082 Query string
1083 }
1084
1085 func (e *QueryMatchesMainModuleError) Error() string {
1086 if e.Pattern == Target.Path {
1087 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
1088 }
1089
1090 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path)
1091 }
1092
1093
1094
1095 type QueryMatchesPackagesInMainModuleError struct {
1096 Pattern string
1097 Query string
1098 Packages []string
1099 }
1100
1101 func (e *QueryMatchesPackagesInMainModuleError) Error() string {
1102 if len(e.Packages) > 1 {
1103 return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
1104 }
1105
1106 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
1107 return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
1108 }
1109
1110 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
1111 }
1112
View as plain text