internal: put build context information in a single place

In forthcoming CLs, we will need to manipulate and compare
Go build contexts (GOOS/GOARCH pairs). Define a struct
for them and put it and support functions in a common
package.

Change-Id: I15d00556fa39e32d1db367f4d33276632300dcbe
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/288213
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Jamal Carvalho <jamal@golang.org>
diff --git a/internal/build_context.go b/internal/build_context.go
new file mode 100644
index 0000000..d5c4a42
--- /dev/null
+++ b/internal/build_context.go
@@ -0,0 +1,42 @@
+// Copyright 2021 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 internal
+
+// A BuildContext describes a build context for the Go tool: information needed
+// to build a Go package. For our purposes, we only care about the information
+// that affects documentation generated from the package.
+type BuildContext struct {
+	GOOS, GOARCH string
+}
+
+// BuildContexts are the build contexts we check when loading a package (see
+// internal/fetch/load.go).
+// We store documentation for all of the listed contexts.
+// The order determines which environment's docs we will show as the default.
+var BuildContexts = []BuildContext{
+	{"linux", "amd64"},
+	{"windows", "amd64"},
+	{"darwin", "amd64"},
+	{"js", "wasm"},
+}
+
+// CompareBuildContexts returns a negative number, 0, or a positive number depending on
+// the relative positions of c1 and c2 in BuildContexts.
+func CompareBuildContexts(c1, c2 BuildContext) int {
+	pos := func(c BuildContext) int {
+		for i, d := range BuildContexts {
+			if c == d {
+				return i
+			}
+		}
+		return len(BuildContexts) // unknowns sort last
+	}
+	return pos(c1) - pos(c2)
+}
+
+// GoBuildContext returns the BuildContext for d.
+func (d *Documentation) BuildContext() BuildContext {
+	return BuildContext{GOOS: d.GOOS, GOARCH: d.GOARCH}
+}
diff --git a/internal/build_context_test.go b/internal/build_context_test.go
new file mode 100644
index 0000000..c4e1fc1
--- /dev/null
+++ b/internal/build_context_test.go
@@ -0,0 +1,27 @@
+// Copyright 2021 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 internal
+
+import "testing"
+
+func TestCompareBuildContexts(t *testing.T) {
+	for i, c1 := range BuildContexts {
+		if got := CompareBuildContexts(c1, c1); got != 0 {
+			t.Errorf("%v: got %d, want 0", c1, got)
+		}
+		for _, c2 := range BuildContexts[i+1:] {
+			if got := CompareBuildContexts(c1, c2); got >= 0 {
+				t.Errorf("%v, %v: got %d, want < 0", c1, c2, got)
+			}
+			if got := CompareBuildContexts(c2, c1); got <= 0 {
+				t.Errorf("%v, %v: got %d, want > 0", c2, c1, got)
+			}
+		}
+	}
+	got := CompareBuildContexts(BuildContext{"?", "?"}, BuildContexts[len(BuildContexts)-1])
+	if got <= 0 {
+		t.Errorf("unknown vs. last: got %d, want > 0", got)
+	}
+}
diff --git a/internal/fetch/load.go b/internal/fetch/load.go
index 3424ef9..0f7edbc 100644
--- a/internal/fetch/load.go
+++ b/internal/fetch/load.go
@@ -44,14 +44,6 @@
 
 func (bpe *BadPackageError) Error() string { return bpe.Err.Error() }
 
-// Go environments used to construct build contexts in loadPackage.
-var goEnvs = []struct{ GOOS, GOARCH string }{
-	{"linux", "amd64"},
-	{"windows", "amd64"},
-	{"darwin", "amd64"},
-	{"js", "wasm"},
-}
-
 // loadPackage loads a Go package by calling loadPackageWithBuildContext, trying
 // several build contexts in turn. It returns a goPackage with documentation
 // information for each build context that results in a valid package, in the
@@ -76,8 +68,8 @@
 		files[name] = b
 	}
 
-	for _, env := range goEnvs {
-		pkg, err := loadPackageWithBuildContext(ctx, env.GOOS, env.GOARCH, files, innerPath, sourceInfo, modInfo)
+	for _, bc := range internal.BuildContexts {
+		pkg, err := loadPackageWithBuildContext(ctx, bc.GOOS, bc.GOARCH, files, innerPath, sourceInfo, modInfo)
 		if err != nil && !errors.Is(err, godoc.ErrTooLarge) && !errors.Is(err, derrors.NotFound) {
 			return nil, err
 		}