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 (
	
	
	
	

	
	
	
	
	
	
	
	
)

fullPath is the full import path corresponding to the requested package/module/directory page.
modulePath is the path of the module corresponding to the fullPath and resolvedVersion. If unknown, it is set to internal.UnknownModulePath.
requestedVersion is the version requested by the user, which will be one of the following: "latest", "master", a Go version tag, or a semantic version.
	requestedVersion string
}

type userError struct {
	userMessage string
	err         error
}

func ( *userError) () string {
	return .err.Error()
}

func ( *userError) () error {
	return .err
}
extractURLPathInfo extracts information from a request to pkg.go.dev. If an error is returned, the user will be served an http.StatusBadRequest.
func ( string) ( *urlPathInfo,  error) {
	defer derrors.Wrap(&, "extractURLPathInfo(%q)", )

	 := strings.SplitN(strings.TrimPrefix(, "/"), "@", 2)
	if stdlib.Contains([0]) {
		return parseStdLibURLPath()
	}
	return parseDetailsURLPath()
}
parseDetailsURLPath parses a URL path that refers (or may refer) to something in the Go ecosystem. After trimming leading and trailing slashes, the path is expected to have one of three forms, and we divide it into three parts: a full path, a module path, and a version. 1. The path has no '@', like github.com/hashicorp/vault/api. This is the full path. The module path is unknown. So is the version, so we treat it as the latest version for whatever the path denotes. 2. The path has "@version" at the end, like github.com/hashicorp/vault/api@v1.2.3. We split this at the '@' into a full path (github.com/hashicorp/vault/api) and version (v1.2.3); the module path is still unknown. 3. The path has "@version" in the middle, like github.com/hashicorp/vault@v1.2.3/api. (We call this the "canonical" form of a path.) We remove the version to get the full path, which is again github.com/hashicorp/vault/api. The version is v1.2.3, and the module path is the part before the '@', github.com/hashicorp/vault. In one case, we do a little more than parse the urlPath into parts: if the full path could be a part of the standard library (because it has no '.'), we assume it is and set the modulePath to indicate the standard library.
func ( string) ( *urlPathInfo,  error) {
	defer derrors.Wrap(&, "parseDetailsURLPath(%q)", )
This splits urlPath into either: /<module-path>[/<suffix>] or /<module-path>, @<version>/<suffix> or /<module-path>/<suffix>, @<version>
The urlPath contains a "@". Parse the version and suffix from parts[1], the string after the '@'.
		 := strings.Split([1], "/")
Parse the requestedVersion from the urlPath. The first path component after the '@' is the version. You cannot explicitly write "latest" for the version.
		if [0] == internal.LatestVersion {
			return nil, &userError{
				err:         fmt.Errorf("invalid version: %q", .requestedVersion),
				userMessage: fmt.Sprintf("%q is not a valid version", [0]),
			}
		}
		.requestedVersion = [0]
Parse the suffix following the "@version" from the urlPath.
		 := strings.Join([1:], "/")
If "@version" occurred in the middle of the path, the part before it is the module path.
			.modulePath = .fullPath
			.fullPath = .fullPath + "/" + 
		}
	}
	if !isValidPath(.fullPath) {
		return nil, &userError{
			err:         fmt.Errorf("isValidPath(%q) is false", .fullPath),
			userMessage: fmt.Sprintf("%q is not a valid import path", .fullPath),
		}
	}
	return , nil
}

func ( string) ( *urlPathInfo,  error) {
	defer derrors.Wrap(&, "parseStdLibURLPath(%q)", )
This splits urlPath into either: /<path>@<tag> or /<path>
	 := strings.SplitN(, "@", 2)
	 := strings.TrimSuffix(strings.TrimPrefix([0], "/"), "/")
	if !isValidPath() {
		return nil, &userError{
			err:         fmt.Errorf("isValidPath(%q) is false", ),
			userMessage: fmt.Sprintf("%q is not a valid import path", ),
		}
	}

	 := &urlPathInfo{
		fullPath:   ,
		modulePath: stdlib.ModulePath,
	}
	if len() == 1 {
		.requestedVersion = internal.LatestVersion
		return , nil
	}
	 := strings.TrimSuffix([1], "/")
	.requestedVersion = stdlib.VersionForTag()
	if .requestedVersion == "" {
		return nil, &userError{
			err:         fmt.Errorf("invalid Go tag for url: %q", ),
			userMessage: fmt.Sprintf("%q is not a valid tag for the standard library", ),
		}
	}
	return , nil
}
isValidPath reports whether a requested path could be a valid unit.
func ( string) bool {
	if  := module.CheckImportPath();  != nil {
		return false
	}
	 := strings.Split(, "/")
	if [0] == "golang.org" {
		if len() < 2 {
			return false
		}
		switch [1] {
		case "dl":
			return true
		case "x":
			return len() >= 3
		default:
			return false
		}
	}
	if vcsHostsWithThreeElementRepoName[[0]] && len() < 3 {
		return false
	}
	return true
}

func ( context.Context,  internal.DataSource,  string) error {
	,  := .(*postgres.DB)
	if ! {
		return nil
	}
	,  := .IsExcluded(, )
	if  != nil {
		return 
	}
Return NotFound; don't let the user know that the package was excluded.
		return &serverError{status: http.StatusNotFound}
	}
	return nil
}
isSupportedVersion reports whether the version is supported by the frontend.
func (,  string) bool {
	if ,  := internal.DefaultBranches[];  {
		return !stdlib.Contains() ||  == "master"
	}
	return  == internal.LatestVersion || semver.IsValid()
}

func ( context.Context,  *http.Request) context.Context {
	if  := .ParseForm();  != nil {
		log.Errorf(, "ParseForm: %v", )
		return 
	}
	return newContextFromExps(, .Form["exp"])
}
newContextFromExps adds and removes experiments from the context's experiment set, creates a new set with the changes, and returns a context with the new set. Each string in expMods can be either an experiment name, which means that the experiment should be added, or "!" followed by an experiment name, meaning that it should be removed.
func ( context.Context,  []string) context.Context {
	var (
		   []string
		 = map[string]bool{}
	)
	 := experiment.FromContext()
	for ,  := range  {
		if strings.HasPrefix(, "!") {
			 = [1:]
			if .IsActive() {
				[] = true
			}
		} else if !.IsActive() {
			 = append(, )
		}
	}
	if len() == 0 && len() == 0 {
		return 
	}
	for ,  := range .Active() {
		if ![] {
			 = append(, )
		}
	}
	return experiment.NewContext(, ...)