1// Copyright 2023 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package gover 6 7import ( 8 "sort" 9 "strings" 10 11 "golang.org/x/mod/module" 12 "golang.org/x/mod/semver" 13) 14 15// IsToolchain reports whether the module path corresponds to the 16// virtual, non-downloadable module tracking go or toolchain directives in the go.mod file. 17// 18// Note that IsToolchain only matches "go" and "toolchain", not the 19// real, downloadable module "golang.org/toolchain" containing toolchain files. 20// 21// IsToolchain("go") = true 22// IsToolchain("toolchain") = true 23// IsToolchain("golang.org/x/tools") = false 24// IsToolchain("golang.org/toolchain") = false 25func IsToolchain(path string) bool { 26 return path == "go" || path == "toolchain" 27} 28 29// ModCompare returns the result of comparing the versions x and y 30// for the module with the given path. 31// The path is necessary because the "go" and "toolchain" modules 32// use a different version syntax and semantics (gover, this package) 33// than most modules (semver). 34func ModCompare(path string, x, y string) int { 35 if path == "go" { 36 return Compare(x, y) 37 } 38 if path == "toolchain" { 39 return Compare(maybeToolchainVersion(x), maybeToolchainVersion(y)) 40 } 41 return semver.Compare(x, y) 42} 43 44// ModSort is like module.Sort but understands the "go" and "toolchain" 45// modules and their version ordering. 46func ModSort(list []module.Version) { 47 sort.Slice(list, func(i, j int) bool { 48 mi := list[i] 49 mj := list[j] 50 if mi.Path != mj.Path { 51 return mi.Path < mj.Path 52 } 53 // To help go.sum formatting, allow version/file. 54 // Compare semver prefix by semver rules, 55 // file by string order. 56 vi := mi.Version 57 vj := mj.Version 58 var fi, fj string 59 if k := strings.Index(vi, "/"); k >= 0 { 60 vi, fi = vi[:k], vi[k:] 61 } 62 if k := strings.Index(vj, "/"); k >= 0 { 63 vj, fj = vj[:k], vj[k:] 64 } 65 if vi != vj { 66 return ModCompare(mi.Path, vi, vj) < 0 67 } 68 return fi < fj 69 }) 70} 71 72// ModIsValid reports whether vers is a valid version syntax for the module with the given path. 73func ModIsValid(path, vers string) bool { 74 if IsToolchain(path) { 75 if path == "toolchain" { 76 return IsValid(FromToolchain(vers)) 77 } 78 return IsValid(vers) 79 } 80 return semver.IsValid(vers) 81} 82 83// ModIsPrefix reports whether v is a valid version syntax prefix for the module with the given path. 84// The caller is assumed to have checked that ModIsValid(path, vers) is true. 85func ModIsPrefix(path, vers string) bool { 86 if IsToolchain(path) { 87 if path == "toolchain" { 88 return IsLang(FromToolchain(vers)) 89 } 90 return IsLang(vers) 91 } 92 // Semver 93 dots := 0 94 for i := 0; i < len(vers); i++ { 95 switch vers[i] { 96 case '-', '+': 97 return false 98 case '.': 99 dots++ 100 if dots >= 2 { 101 return false 102 } 103 } 104 } 105 return true 106} 107 108// ModIsPrerelease reports whether v is a prerelease version for the module with the given path. 109// The caller is assumed to have checked that ModIsValid(path, vers) is true. 110func ModIsPrerelease(path, vers string) bool { 111 if IsToolchain(path) { 112 return IsPrerelease(vers) 113 } 114 return semver.Prerelease(vers) != "" 115} 116 117// ModMajorMinor returns the "major.minor" truncation of the version v, 118// for use as a prefix in "@patch" queries. 119func ModMajorMinor(path, vers string) string { 120 if IsToolchain(path) { 121 if path == "toolchain" { 122 return "go" + Lang(FromToolchain(vers)) 123 } 124 return Lang(vers) 125 } 126 return semver.MajorMinor(vers) 127} 128