// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gover import ( "sort" "strings" "golang.org/x/mod/module" "golang.org/x/mod/semver" ) // IsToolchain reports whether the module path corresponds to the // virtual, non-downloadable module tracking go or toolchain directives in the go.mod file. // // Note that IsToolchain only matches "go" and "toolchain", not the // real, downloadable module "golang.org/toolchain" containing toolchain files. // // IsToolchain("go") = true // IsToolchain("toolchain") = true // IsToolchain("golang.org/x/tools") = false // IsToolchain("golang.org/toolchain") = false func IsToolchain(path string) bool { return path == "go" || path == "toolchain" } // ModCompare returns the result of comparing the versions x and y // for the module with the given path. // The path is necessary because the "go" and "toolchain" modules // use a different version syntax and semantics (gover, this package) // than most modules (semver). func ModCompare(path string, x, y string) int { if path == "go" { return Compare(x, y) } if path == "toolchain" { return Compare(maybeToolchainVersion(x), maybeToolchainVersion(y)) } return semver.Compare(x, y) } // ModSort is like module.Sort but understands the "go" and "toolchain" // modules and their version ordering. func ModSort(list []module.Version) { sort.Slice(list, func(i, j int) bool { mi := list[i] mj := list[j] if mi.Path != mj.Path { return mi.Path < mj.Path } // To help go.sum formatting, allow version/file. // Compare semver prefix by semver rules, // file by string order. vi := mi.Version vj := mj.Version var fi, fj string if k := strings.Index(vi, "/"); k >= 0 { vi, fi = vi[:k], vi[k:] } if k := strings.Index(vj, "/"); k >= 0 { vj, fj = vj[:k], vj[k:] } if vi != vj { return ModCompare(mi.Path, vi, vj) < 0 } return fi < fj }) } // ModIsValid reports whether vers is a valid version syntax for the module with the given path. func ModIsValid(path, vers string) bool { if IsToolchain(path) { if path == "toolchain" { return IsValid(FromToolchain(vers)) } return IsValid(vers) } return semver.IsValid(vers) } // ModIsPrefix reports whether v is a valid version syntax prefix for the module with the given path. // The caller is assumed to have checked that ModIsValid(path, vers) is true. func ModIsPrefix(path, vers string) bool { if IsToolchain(path) { if path == "toolchain" { return IsLang(FromToolchain(vers)) } return IsLang(vers) } // Semver dots := 0 for i := 0; i < len(vers); i++ { switch vers[i] { case '-', '+': return false case '.': dots++ if dots >= 2 { return false } } } return true } // ModIsPrerelease reports whether v is a prerelease version for the module with the given path. // The caller is assumed to have checked that ModIsValid(path, vers) is true. func ModIsPrerelease(path, vers string) bool { if IsToolchain(path) { return IsPrerelease(vers) } return semver.Prerelease(vers) != "" } // ModMajorMinor returns the "major.minor" truncation of the version v, // for use as a prefix in "@patch" queries. func ModMajorMinor(path, vers string) string { if IsToolchain(path) { if path == "toolchain" { return "go" + Lang(FromToolchain(vers)) } return Lang(vers) } return semver.MajorMinor(vers) }