Copyright 2019 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 frontend

import (
	
	
	
	
	
	

	
	
	
	
	
	
	
)
VersionsDetails contains the hierarchy of version summary information used to populate the version tab. Version information is organized into separate lists, one for each (ModulePath, Major Version) pair.
ThisModule is the slice of VersionLists with the same module path as the current package.
IncompatibleModules is the slice of the VersionsLists with the same module path as the current package, but with incompatible versions.
OtherModules is the slice of VersionLists with a different module path from the current package.
VersionListKey identifies a version list on the versions tab. We have a separate VersionList for each major version of a module series. Notably we have more version lists than module paths: v0 and v1 module versions are in separate version lists, despite having the same module path.
ModulePath is the module path of this major version.
Major is the major version string (e.g. v1, v2)
Incompatible indicates whether the VersionListKey represents an incompatible module version.
Deprecated indicates whether the major version is deprecated.
DeprecationComment holds the reason for deprecation, if any.
VersionList holds all versions corresponding to a unique (module path, major version) tuple in the version hierarchy.
type VersionList struct {
Versions holds the nested version summaries, organized in descending semver order.
VersionSummary holds data required to format the version link on the versions tab.
type VersionSummary struct {
Link to this version, for use in the anchor href.
The proxydatasource does not support the imported by page.
		return nil, proxydatasourceNotSupportedErr()
	}
	,  := .GetVersionsForPath(, )
	if  != nil {
		return nil, 
	}

	 := internal.NewSymbolHistory()
	if  == stdlib.ModulePath || experiment.IsActive(, internal.ExperimentSymbolHistoryVersionsPage) {
		,  = .GetSymbolHistory(, , )
		if  != nil {
			return nil, 
		}
	}
Here we have only version information, but need to construct the full import path of the package corresponding to this version.
		var  string
		if .ModulePath == stdlib.ModulePath {
			 = 
		} else {
			 = pathInVersion(internal.V1Path(, ), )
		}
		return constructUnitURL(, .ModulePath, linkVersion(.Version, .ModulePath))
	}
	return buildVersionDetails(, , , ), nil
}
pathInVersion constructs the full import path of the package corresponding to mi, given its v1 path. To do this, we first compute the suffix of the package path in the given module series, and then append it to the real (versioned) module path. For example: if we're considering package foo.com/v3/bar/baz, and encounter module version foo.com/bar/v2, we do the following: 1) Start with the v1Path foo.com/bar/baz. 2) Trim off the version series path foo.com/bar to get 'baz'. 3) Join with the versioned module path foo.com/bar/v2 to get foo.com/bar/v2/baz. ...being careful about slashes along the way.
func ( string,  *internal.ModuleInfo) string {
	 := internal.Suffix(, .SeriesPath())
	if  == "" {
		return .ModulePath
	}
	return path.Join(.ModulePath, )
}
buildVersionDetails constructs the version hierarchy to be rendered on the versions tab, organizing major versions into those that have the same module path as the package version under consideration, and those that don't. The given versions MUST be sorted first by module path and then by semver.
func ( string,
	 []*internal.ModuleInfo,
	 *internal.SymbolHistory,
lists organizes versions by VersionListKey. Note that major version isn't sufficient as a key: there are packages contained in the same major version of different modules, for example github.com/hashicorp/vault/api, which exists in v1 of both of github.com/hashicorp/vault and github.com/hashicorp/vault/api.
seenLists tracks the order in which we encounter entries of each version list. We want to preserve this order.
	var  []VersionListKey
Try to resolve the most appropriate major version for this version. If we detect a +incompatible version (when the path version does not match the sematic version), we prefer the path version.
		 := semver.Major(.Version)
		if .ModulePath == stdlib.ModulePath {
			var  error
			,  = stdlib.MajorVersionForVersion(.Version)
			if  != nil {
				panic()
			}
We prefer the path major version except for v1 import paths where the semver major version is v0. In this case, we prefer the more specific semver version.
		 := internal.MajorVersionForModule(.ModulePath)
		if  != "" {
			 = 
		} else if version.IsIncompatible(.Version) {
			 = semver.Major(.Version)
		} else if  != "v0" && !strings.HasPrefix(, "go") {
			 = "v1"
		}
		 := VersionListKey{
			ModulePath:   .ModulePath,
			Major:        ,
			Incompatible: version.IsIncompatible(.Version),
		}
		 := &VersionSummary{
			Link:       (),
			CommitTime: absoluteTime(.CommitTime),
			Version:    linkVersion(.Version, .ModulePath),
			IsMinor:    isMinor(.Version),
		}
		.Deprecated = .Deprecated
		.DeprecationComment = shortRationale(.DeprecationComment)
		.Retracted = .Retracted
		.RetractionRationale = shortRationale(.RetractionRationale)
		if  := .SymbolsAtVersion(.Version);  != nil {
			.Symbols = symbolsForVersion((), )
		}
		if ,  := []; ! {
			 = append(, )
		}
		[] = append([], )
	}

	var  VersionsDetails
	 := map[string]bool{}
	for ,  := range  {
		 := &VersionList{
			VersionListKey: ,
			Versions:       [],
		}
		if .ModulePath ==  {
			if .Incompatible {
				.IncompatibleModules = append(.IncompatibleModules, )
			} else {
				.ThisModule = append(.ThisModule, )
			}
		} else {
			[.ModulePath] = true
		}
	}
	for  := range  {
		.OtherModules = append(.OtherModules, )
Sort for testing.
	sort.Strings(.OtherModules)
	return &
}
isMinor reports whether v is a release version where the patch version is 0. It is assumed that v is a valid semantic version.
func ( string) bool {
	if version.IsIncompatible() {
		return false
	}
	,  := version.ParseType()
This should never happen because v will always be a valid semantic version.
		return false
	}
	if  == version.TypePrerelease ||  == version.TypePseudo {
		return false
	}
	return strings.HasSuffix(strings.TrimPrefix(, semver.MajorMinor()), ".0")
}
formatVersion formats a more readable representation of the given version string. On any parsing error, it simply returns the input unmodified. For pseudo versions, the version string will use a shorten commit hash of 7 characters to identify the version, and hide timestamp using ellipses. For any version string longer than 25 characters, the pre-release string will be truncated, such that the string displayed is exactly 25 characters, including the ellipses. See TestFormatVersion for examples.
func ( string) string {
	const  = 25
	if len() <=  {
		return 
	}
	,  := version.ParseType()
	if  != nil {
		log.Errorf(context.TODO(), "formatVersion(%q): error parsing version: %v", , )
		return 
	}
If the version is release or prerelease, return a version string of maxLen by truncating the end of the string. maxLen is inclusive of the "..." characters.
		return [:-3] + "..."
	}
The version string will have a max length of 25: base: "vX.Y.Z-prerelease.0" = up to 15 ellipse: "..." = 3 commit: "-abcdefa" = 7
	 := shorten(pseudoVersionRev(), 7)
	 := shorten(pseudoVersionBase(), 15)
	return fmt.Sprintf("%s...-%s", , )
}
shorten shortens the string s to maxLen by removing the trailing characters.
func ( string,  int) string {
	if len() >  {
		return [:]
	}
	return 
}
shortRationale returns a rationale string that is safe to print in a terminal. It returns hard-coded strings if the rationale is empty, too long, or contains non-printable characters.
Copied with slight modifications from https://go.googlesource.com/go/+/87c6fa4f473f178f7d931ddadd10c76444f8dc7b/src/cmd/go/internal/modload/modfile.go#208.
	const  = 500
	if  := strings.Index(, "\n");  >= 0 {
		 = [:]
	}
	 = strings.TrimSpace()
	if  == "" {
		return ""
	}
	if len() >  {
		return "(rationale omitted: too long)"
	}
	for ,  := range  {
		if !unicode.IsGraphic() && !unicode.IsSpace() {
			return "(rationale omitted: contains non-printable characters)"
		}
NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
	return 
}
pseudoVersionRev extracts the pseudo version base, excluding the timestamp. It assumes the pseudo version is correctly formatted. See TestPseudoVersionBase for examples.
func ( string) string {
	 := strings.Split(, "-")
	if len() != 3 {
		 := strings.Join([1:len()-1], "-")
		 = []string{[0], , [2]}
The version string will always be split into one of these 3 parts: 1. [vX.0.0, yyyymmddhhmmss, abcdefabcdef] 2. [vX.Y.Z, pre.0.yyyymmddhhmmss, abcdefabcdef] 3. [vX.Y.Z, 0.yyyymmddhhmmss, abcdefabcdef]
	 := strings.Split([1], ".")
	var  string
There is a "pre.0" or "0" prefix in the second element.
		 = strings.Join([0:len()-1], ".")
	}
	return fmt.Sprintf("%s-%s", [0], )
}
pseudoVersionRev extracts the first 7 characters of the commit identifier from a pseudo version string. It assumes the pseudo version is correctly formatted.
func ( string) string {
	 = strings.TrimSuffix(, "+incompatible")
	 := strings.LastIndex(, "-")
	return [+1:]
}
displayVersion returns the version string, formatted for display.
func ( string,  string) string {
	if  == stdlib.ModulePath {
		if strings.HasPrefix(, "v0.0.0") {
			return strings.Split(, "-")[2]
		}
		return goTagForVersion()
	}
	return formatVersion()
}
linkVersion returns the version string, suitable for use in a link to this site. TODO(golang/go#41855): Clarify definition / use case for linkVersion and other version strings.
func ( string,  string) string {
	if  == stdlib.ModulePath {
		if strings.HasPrefix(, "go") {
			return 
		}
		return goTagForVersion()
	}
	return 
}
goTagForVersion returns the Go tag corresponding to a given semantic version. It should only be used if we are 100% sure the version will correspond to a Go tag, such as when we are fetching the version from the database.
func ( string) string {
	,  := stdlib.TagForVersion()
	if  != nil {
		log.Errorf(context.TODO(), "goTagForVersion(%q): %v", , )
		return "unknown"
	}
	return