1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "io/fs"
13 "os"
14 pathpkg "path"
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") && module.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 := module.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 = module.IsPseudoVersion(current)
329 qm.filter = func(mv string) bool { return semver.Compare(mv, current) >= 0 }
330 }
331
332 case query == "patch":
333 if current == "" || current == "none" {
334 return nil, &NoPatchBaseError{path}
335 }
336 if current == "" {
337 qm.mayUseLatest = true
338 } else {
339 qm.mayUseLatest = module.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
700
701 func modulePrefixesExcludingTarget(path string) []string {
702 prefixes := make([]string, 0, strings.Count(path, "/")+1)
703
704 for {
705 if path != targetPrefix {
706 if _, _, ok := module.SplitPathVersion(path); ok {
707 prefixes = append(prefixes, path)
708 }
709 }
710
711 j := strings.LastIndexByte(path, '/')
712 if j < 0 {
713 break
714 }
715 path = path[:j]
716 }
717
718 return prefixes
719 }
720
721 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
722 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
723 defer span.Done()
724
725
726
727
728
729 type result struct {
730 QueryResult
731 err error
732 }
733 results := make([]result, len(candidateModules))
734 var wg sync.WaitGroup
735 wg.Add(len(candidateModules))
736 for i, p := range candidateModules {
737 ctx := trace.StartGoroutine(ctx)
738 go func(p string, r *result) {
739 r.QueryResult, r.err = queryModule(ctx, p)
740 wg.Done()
741 }(p, &results[i])
742 }
743 wg.Wait()
744
745
746
747
748 var (
749 noPackage *PackageNotInModuleError
750 noVersion *NoMatchingVersionError
751 noPatchBase *NoPatchBaseError
752 invalidPath *module.InvalidPathError
753 notExistErr error
754 )
755 for _, r := range results {
756 switch rErr := r.err.(type) {
757 case nil:
758 found = append(found, r.QueryResult)
759 case *PackageNotInModuleError:
760
761
762 if noPackage == nil || noPackage.Mod == Target {
763 noPackage = rErr
764 }
765 case *NoMatchingVersionError:
766 if noVersion == nil {
767 noVersion = rErr
768 }
769 case *NoPatchBaseError:
770 if noPatchBase == nil {
771 noPatchBase = rErr
772 }
773 case *module.InvalidPathError:
774
775
776
777
778
779
780
781 if invalidPath == nil {
782 invalidPath = rErr
783 }
784 default:
785 if errors.Is(rErr, fs.ErrNotExist) {
786 if notExistErr == nil {
787 notExistErr = rErr
788 }
789 } else if err == nil {
790 if len(found) > 0 || noPackage != nil {
791
792
793
794
795
796
797
798 } else {
799 err = r.err
800 }
801 }
802 }
803 }
804
805
806
807
808
809 if len(found) == 0 && err == nil {
810 switch {
811 case noPackage != nil:
812 err = noPackage
813 case noVersion != nil:
814 err = noVersion
815 case noPatchBase != nil:
816 err = noPatchBase
817 case invalidPath != nil:
818 err = invalidPath
819 case notExistErr != nil:
820 err = notExistErr
821 default:
822 panic("queryPrefixModules: no modules found, but no error detected")
823 }
824 }
825
826 return found, err
827 }
828
829
830
831
832
833
834
835
836
837 type NoMatchingVersionError struct {
838 query, current string
839 }
840
841 func (e *NoMatchingVersionError) Error() string {
842 currentSuffix := ""
843 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
844 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
845 }
846 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
847 }
848
849
850
851 type NoPatchBaseError struct {
852 path string
853 }
854
855 func (e *NoPatchBaseError) Error() string {
856 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
857 }
858
859
860
861
862 type WildcardInFirstElementError struct {
863 Pattern string
864 Query string
865 }
866
867 func (e *WildcardInFirstElementError) Error() string {
868 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
869 }
870
871
872
873
874
875
876
877
878
879
880 type PackageNotInModuleError struct {
881 Mod module.Version
882 Replacement module.Version
883 Query string
884 Pattern string
885 }
886
887 func (e *PackageNotInModuleError) Error() string {
888 if e.Mod == Target {
889 if strings.Contains(e.Pattern, "...") {
890 return fmt.Sprintf("main module (%s) does not contain packages matching %s", Target.Path, e.Pattern)
891 }
892 return fmt.Sprintf("main module (%s) does not contain package %s", Target.Path, e.Pattern)
893 }
894
895 found := ""
896 if r := e.Replacement; r.Path != "" {
897 replacement := r.Path
898 if r.Version != "" {
899 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
900 }
901 if e.Query == e.Mod.Version {
902 found = fmt.Sprintf(" (replaced by %s)", replacement)
903 } else {
904 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
905 }
906 } else if e.Query != e.Mod.Version {
907 found = fmt.Sprintf(" (%s)", e.Mod.Version)
908 }
909
910 if strings.Contains(e.Pattern, "...") {
911 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
912 }
913 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
914 }
915
916 func (e *PackageNotInModuleError) ImportPath() string {
917 if !strings.Contains(e.Pattern, "...") {
918 return e.Pattern
919 }
920 return ""
921 }
922
923
924 func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) {
925 needSum := false
926 root, isLocal, err := fetch(ctx, m, needSum)
927 if err != nil {
928 return false, err
929 }
930 _, ok, err := dirInModule(m.Path, m.Path, root, isLocal)
931 return ok, err
932 }
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
954 _, data, err := rawGoModData(m)
955 if err != nil {
956 return false, err
957 }
958 isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
959 return !isFake, nil
960 }
961
962
963
964 type versionRepo interface {
965 ModulePath() string
966 Versions(prefix string) ([]string, error)
967 Stat(rev string) (*modfetch.RevInfo, error)
968 Latest() (*modfetch.RevInfo, error)
969 }
970
971 var _ versionRepo = modfetch.Repo(nil)
972
973 func lookupRepo(proxy, path string) (repo versionRepo, err error) {
974 err = module.CheckPath(path)
975 if err == nil {
976 repo = modfetch.Lookup(proxy, path)
977 } else {
978 repo = emptyRepo{path: path, err: err}
979 }
980
981 if index == nil {
982 return repo, err
983 }
984 if _, ok := index.highestReplaced[path]; !ok {
985 return repo, err
986 }
987
988 return &replacementRepo{repo: repo}, nil
989 }
990
991
992 type emptyRepo struct {
993 path string
994 err error
995 }
996
997 var _ versionRepo = emptyRepo{}
998
999 func (er emptyRepo) ModulePath() string { return er.path }
1000 func (er emptyRepo) Versions(prefix string) ([]string, error) { return nil, nil }
1001 func (er emptyRepo) Stat(rev string) (*modfetch.RevInfo, error) { return nil, er.err }
1002 func (er emptyRepo) Latest() (*modfetch.RevInfo, error) { return nil, er.err }
1003
1004
1005
1006
1007
1008
1009
1010 type replacementRepo struct {
1011 repo versionRepo
1012 }
1013
1014 var _ versionRepo = (*replacementRepo)(nil)
1015
1016 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
1017
1018
1019
1020 func (rr *replacementRepo) Versions(prefix string) ([]string, error) {
1021 repoVersions, err := rr.repo.Versions(prefix)
1022 if err != nil && !errors.Is(err, os.ErrNotExist) {
1023 return nil, err
1024 }
1025
1026 versions := repoVersions
1027 if index != nil && len(index.replace) > 0 {
1028 path := rr.ModulePath()
1029 for m, _ := range index.replace {
1030 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
1031 versions = append(versions, m.Version)
1032 }
1033 }
1034 }
1035
1036 if len(versions) == len(repoVersions) {
1037 return versions, nil
1038 }
1039
1040 sort.Slice(versions, func(i, j int) bool {
1041 return semver.Compare(versions[i], versions[j]) < 0
1042 })
1043 str.Uniq(&versions)
1044 return versions, nil
1045 }
1046
1047 func (rr *replacementRepo) Stat(rev string) (*modfetch.RevInfo, error) {
1048 info, err := rr.repo.Stat(rev)
1049 if err == nil || index == nil || len(index.replace) == 0 {
1050 return info, err
1051 }
1052
1053 v := module.CanonicalVersion(rev)
1054 if v != rev {
1055
1056
1057 return info, err
1058 }
1059
1060 path := rr.ModulePath()
1061 _, pathMajor, ok := module.SplitPathVersion(path)
1062 if ok && pathMajor == "" {
1063 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
1064 v += "+incompatible"
1065 }
1066 }
1067
1068 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1069 return info, err
1070 }
1071 return rr.replacementStat(v)
1072 }
1073
1074 func (rr *replacementRepo) Latest() (*modfetch.RevInfo, error) {
1075 info, err := rr.repo.Latest()
1076
1077 if index != nil {
1078 path := rr.ModulePath()
1079 if v, ok := index.highestReplaced[path]; ok {
1080 if v == "" {
1081
1082
1083
1084
1085
1086 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
1087 v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
1088 } else {
1089 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
1090 }
1091 }
1092
1093 if err != nil || semver.Compare(v, info.Version) > 0 {
1094 return rr.replacementStat(v)
1095 }
1096 }
1097 }
1098
1099 return info, err
1100 }
1101
1102 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
1103 rev := &modfetch.RevInfo{Version: v}
1104 if module.IsPseudoVersion(v) {
1105 rev.Time, _ = module.PseudoVersionTime(v)
1106 rev.Short, _ = module.PseudoVersionRev(v)
1107 }
1108 return rev, nil
1109 }
1110
1111
1112
1113
1114 type QueryMatchesMainModuleError struct {
1115 Pattern string
1116 Query string
1117 }
1118
1119 func (e *QueryMatchesMainModuleError) Error() string {
1120 if e.Pattern == Target.Path {
1121 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
1122 }
1123
1124 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module (%s)", e.Query, e.Pattern, Target.Path)
1125 }
1126
1127
1128
1129 type QueryMatchesPackagesInMainModuleError struct {
1130 Pattern string
1131 Query string
1132 Packages []string
1133 }
1134
1135 func (e *QueryMatchesPackagesInMainModuleError) Error() string {
1136 if len(e.Packages) > 1 {
1137 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)
1138 }
1139
1140 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
1141 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)
1142 }
1143
1144 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
1145 }
1146
View as plain text