internal/fetch: create goPackage

A fetch.goPackage struct is created to replace uses of
internal.LegacyPackage, which will eventually be deleted.

For golang/go#39629

Change-Id: Ibf8af9732396b60a29f20560db0f9c704c911dd9
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/256139
Trust: Julie Qiu <julie@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/internal/fetch/directory.go b/internal/fetch/directory.go
index b1a8003..d9a916d 100644
--- a/internal/fetch/directory.go
+++ b/internal/fetch/directory.go
@@ -15,12 +15,12 @@
 // moduleUnits returns all of the units in a given module, along
 // with the contents for those units.
 func moduleUnits(modulePath, version string,
-	pkgs []*internal.LegacyPackage,
+	pkgs []*goPackage,
 	readmes []*internal.Readme,
 	d *licenses.Detector) []*internal.Unit {
-	pkgLookup := map[string]*internal.LegacyPackage{}
+	pkgLookup := map[string]*goPackage{}
 	for _, pkg := range pkgs {
-		pkgLookup[pkg.Path] = pkg
+		pkgLookup[pkg.path] = pkg
 	}
 	dirPaths := unitPaths(modulePath, pkgs)
 
@@ -59,13 +59,13 @@
 			dir.Readme = r
 		}
 		if pkg, ok := pkgLookup[dirPath]; ok {
-			dir.Name = pkg.Name
-			dir.Imports = pkg.Imports
+			dir.Name = pkg.name
+			dir.Imports = pkg.imports
 			dir.Documentation = &internal.Documentation{
-				GOOS:     pkg.GOOS,
-				GOARCH:   pkg.GOARCH,
-				Synopsis: pkg.Synopsis,
-				HTML:     pkg.DocumentationHTML,
+				GOOS:     pkg.goos,
+				GOARCH:   pkg.goarch,
+				Synopsis: pkg.synopsis,
+				HTML:     pkg.documentationHTML,
 			}
 		}
 		units = append(units, dir)
@@ -74,7 +74,7 @@
 }
 
 // unitPaths returns the paths for all the units in a module.
-func unitPaths(modulePath string, packages []*internal.LegacyPackage) []string {
+func unitPaths(modulePath string, packages []*goPackage) []string {
 	shouldContinue := func(p string) bool {
 		if modulePath == stdlib.ModulePath {
 			return p != "."
@@ -84,7 +84,7 @@
 
 	pathSet := map[string]bool{modulePath: true}
 	for _, p := range packages {
-		for p := p.Path; shouldContinue(p); p = path.Dir(p) {
+		for p := p.path; shouldContinue(p); p = path.Dir(p) {
 			pathSet[p] = true
 		}
 	}
diff --git a/internal/fetch/directory_test.go b/internal/fetch/directory_test.go
index 142889a..7102ef0 100644
--- a/internal/fetch/directory_test.go
+++ b/internal/fetch/directory_test.go
@@ -5,6 +5,7 @@
 package fetch
 
 import (
+	"path"
 	"sort"
 	"testing"
 
@@ -57,9 +58,9 @@
 		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
-			var packages []*internal.LegacyPackage
+			var packages []*goPackage
 			for _, suffix := range test.packageSuffixes {
-				packages = append(packages, sample.LegacyPackage(test.modulePath, suffix))
+				packages = append(packages, samplePackage(test.modulePath, suffix))
 			}
 			got := unitPaths(test.modulePath, packages)
 			sort.Strings(got)
@@ -70,3 +71,33 @@
 		})
 	}
 }
+
+// samplePackage constructs a package with the given module path and suffix.
+//
+// If modulePath is the standard library, the package path is the
+// suffix, which must not be empty. Otherwise, the package path
+// is the concatenation of modulePath and suffix.
+//
+// The package name is last component of the package path.
+func samplePackage(modulePath, suffix string) *goPackage {
+	p := constructFullPath(modulePath, suffix)
+	return &goPackage{
+		name:              path.Base(p),
+		path:              p,
+		v1path:            internal.V1Path(p, modulePath),
+		synopsis:          sample.Synopsis,
+		isRedistributable: true,
+		licenseMeta:       sample.LicenseMetadata,
+		documentationHTML: sample.DocumentationHTML,
+		imports:           sample.Imports,
+		goos:              sample.GOOS,
+		goarch:            sample.GOARCH,
+	}
+}
+
+func constructFullPath(modulePath, suffix string) string {
+	if modulePath != stdlib.ModulePath {
+		return path.Join(modulePath, suffix)
+	}
+	return suffix
+}
diff --git a/internal/fetch/fetch.go b/internal/fetch/fetch.go
index 25a1437..ba423f1 100644
--- a/internal/fetch/fetch.go
+++ b/internal/fetch/fetch.go
@@ -205,6 +205,21 @@
 		readmeContents = r.Contents
 		break
 	}
+	var legacyPackages []*internal.LegacyPackage
+	for _, p := range packages {
+		legacyPackages = append(legacyPackages, &internal.LegacyPackage{
+			Path:              p.path,
+			Name:              p.name,
+			Synopsis:          p.synopsis,
+			Imports:           p.imports,
+			DocumentationHTML: p.documentationHTML,
+			GOOS:              p.goos,
+			GOARCH:            p.goarch,
+			V1Path:            p.v1path,
+			IsRedistributable: p.isRedistributable,
+			Licenses:          p.licenseMeta,
+		})
+	}
 	return &internal.Module{
 		LegacyModuleInfo: internal.LegacyModuleInfo{
 			ModuleInfo: internal.ModuleInfo{
@@ -218,7 +233,7 @@
 			LegacyReadmeFilePath: readmeFilePath,
 			LegacyReadmeContents: readmeContents,
 		},
-		LegacyPackages: packages,
+		LegacyPackages: legacyPackages,
 		Licenses:       allLicenses,
 		Units:          moduleUnits(modulePath, resolvedVersion, packages, readmes, d),
 	}, packageVersionStates, nil
diff --git a/internal/fetch/load.go b/internal/fetch/load.go
index a9329b4..5636541 100644
--- a/internal/fetch/load.go
+++ b/internal/fetch/load.go
@@ -64,7 +64,7 @@
 //
 // If the package is fine except that its documentation is too large, loadPackage
 // returns both a package and a non-nil error with dochtml.ErrTooLarge in its chain.
-func loadPackage(ctx context.Context, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (*internal.LegacyPackage, error) {
+func loadPackage(ctx context.Context, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (*goPackage, error) {
 	ctx, span := trace.StartSpan(ctx, "fetch.loadPackage")
 	defer span.End()
 	for _, env := range goEnvs {
@@ -93,13 +93,13 @@
 // zipGoFiles must contain only .go files that have been verified
 // to be of reasonable size.
 //
-// The returned LegacyPackage.Licenses field is not populated.
+// The returned Package.Licenses field is not populated.
 //
-// It returns a nil LegacyPackage if the directory doesn't contain a Go package
+// It returns a nil Package if the directory doesn't contain a Go package
 // or all .go files have been excluded by constraints.
 // A *BadPackageError error is returned if the directory
 // contains .go files but do not make up a valid package.
-func loadPackageWithBuildContext(ctx context.Context, goos, goarch string, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (_ *internal.LegacyPackage, err error) {
+func loadPackageWithBuildContext(ctx context.Context, goos, goarch string, zipGoFiles []*zip.File, innerPath string, sourceInfo *source.Info, modInfo *dochtml.ModuleInfo) (_ *goPackage, err error) {
 	modulePath := modInfo.ModulePath
 	defer derrors.Wrap(&err, "loadPackageWithBuildContext(%q, %q, zipGoFiles, %q, %q, %+v)",
 		goos, goarch, innerPath, modulePath, sourceInfo)
@@ -217,15 +217,15 @@
 		importPath = innerPath
 	}
 	v1path := internal.V1Path(importPath, modulePath)
-	return &internal.LegacyPackage{
-		Path:              importPath,
-		Name:              packageName,
-		Synopsis:          doc.Synopsis(d.Doc),
-		V1Path:            v1path,
-		Imports:           d.Imports,
-		DocumentationHTML: docHTML,
-		GOOS:              goos,
-		GOARCH:            goarch,
+	return &goPackage{
+		path:              importPath,
+		name:              packageName,
+		synopsis:          doc.Synopsis(d.Doc),
+		v1path:            v1path,
+		imports:           d.Imports,
+		documentationHTML: docHTML,
+		goos:              goos,
+		goarch:            goarch,
 	}, err
 }
 
diff --git a/internal/fetch/package.go b/internal/fetch/package.go
index 1b6bfa4..f4479fa 100644
--- a/internal/fetch/package.go
+++ b/internal/fetch/package.go
@@ -15,6 +15,7 @@
 	"runtime/debug"
 	"strings"
 
+	"github.com/google/safehtml"
 	"go.opencensus.io/trace"
 	"golang.org/x/mod/module"
 	"golang.org/x/pkgsite/internal"
@@ -25,6 +26,26 @@
 	"golang.org/x/pkgsite/internal/source"
 )
 
+// A goPackage is a group of one or more Go source files with the same
+// package header. Packages are part of a module.
+type goPackage struct {
+	path              string
+	name              string
+	synopsis          string
+	imports           []string
+	documentationHTML safehtml.HTML
+	isRedistributable bool
+	licenseMeta       []*licenses.Metadata // metadata of applicable licenses
+	// goos and goarch are environment variables used to parse the
+	// package.
+	goos   string
+	goarch string
+
+	// v1path is the package path of a package with major version 1 in a given
+	// series.
+	v1path string
+}
+
 // extractPackagesFromZip returns a slice of packages from the module zip r.
 // It matches against the given licenses to determine the subset of licenses
 // that applies to each package.
@@ -34,7 +55,7 @@
 // * a maximum file size (MaxFileSize)
 // * the particular set of build contexts we consider (goEnvs)
 // * whether the import path is valid.
-func extractPackagesFromZip(ctx context.Context, modulePath, resolvedVersion string, r *zip.Reader, d *licenses.Detector, sourceInfo *source.Info) (_ []*internal.LegacyPackage, _ []*internal.PackageVersionState, err error) {
+func extractPackagesFromZip(ctx context.Context, modulePath, resolvedVersion string, r *zip.Reader, d *licenses.Detector, sourceInfo *source.Info) (_ []*goPackage, _ []*internal.PackageVersionState, err error) {
 	ctx, span := trace.StartSpan(ctx, "fetch.extractPackagesFromZip")
 	defer span.End()
 	defer func() {
@@ -159,7 +180,7 @@
 	// If we got this far, the file metadata was okay.
 	// Start reading the file contents now to extract information
 	// about Go packages.
-	var pkgs []*internal.LegacyPackage
+	var pkgs []*goPackage
 	for innerPath, goFiles := range dirs {
 		if incompleteDirs[innerPath] {
 			// Something went wrong when processing this directory, so we skip.
@@ -195,13 +216,13 @@
 		} else {
 			if d != nil { //  should only be nil for tests
 				isRedist, lics := d.PackageInfo(innerPath)
-				pkg.IsRedistributable = isRedist
+				pkg.isRedistributable = isRedist
 				for _, l := range lics {
-					pkg.Licenses = append(pkg.Licenses, l.Metadata)
+					pkg.licenseMeta = append(pkg.licenseMeta, l.Metadata)
 				}
 			}
 			pkgs = append(pkgs, pkg)
-			pkgPath = pkg.Path
+			pkgPath = pkg.path
 		}
 		code := http.StatusOK
 		if status != nil {
@@ -227,7 +248,7 @@
 // The logic of the go tool for ignoring directories is documented at
 // https://golang.org/cmd/go/#hdr-Package_lists_and_patterns:
 //
-// 	LegacyDirectory and file names that begin with "." or "_" are ignored
+// 	Directory and file names that begin with "." or "_" are ignored
 // 	by the go tool, as are directories named "testdata".
 //
 func ignoredByGoTool(importPath string) bool {