diff --git a/internal/godocfx/parse.go b/internal/godocfx/parse.go index 179f82d406b..b1100e3c96b 100644 --- a/internal/godocfx/parse.go +++ b/internal/godocfx/parse.go @@ -49,10 +49,11 @@ type tableOfContents []*tocItem // tocItem is an item in a TOC. type tocItem struct { - UID string `yaml:"uid,omitempty"` - Name string `yaml:"name,omitempty"` - Items []*tocItem `yaml:"items,omitempty"` - Href string `yaml:"href,omitempty"` + UID string `yaml:"uid,omitempty"` + Name string `yaml:"name,omitempty"` + Items []*tocItem `yaml:"items,omitempty"` + Href string `yaml:"href,omitempty"` + Status string `yaml:"status,omitempty"` } func (t *tocItem) addItem(i *tocItem) { @@ -169,6 +170,7 @@ func parse(glob string, workingDir string, optionalExtraFiles []string, filter [ Type: "package", Examples: processExamples(pi.Doc.Examples, pi.Fset), AltLink: "https://pkg.go.dev/" + pi.Doc.ImportPath, + Status: pi.Status, } pkgPage := &page{Items: []*item{pkgItem}} pages[pi.Doc.ImportPath] = pkgPage @@ -545,12 +547,6 @@ func buildTOC(mod string, pis []pkgload.Info, extraFiles []extraFile) tableOfCon Name: mod, } - // Assume the module root has a package. - modTOC.addItem(&tocItem{ - UID: mod, - Name: mod, - }) - for _, ef := range extraFiles { modTOC.addItem(&tocItem{ Href: ef.dstRelativePath, @@ -560,15 +556,17 @@ func buildTOC(mod string, pis []pkgload.Info, extraFiles []extraFile) tableOfCon toc = append(toc, modTOC) - if len(pis) == 1 { - // The module only has one package. - return toc - } - trimmedPkgs := []string{} + statuses := map[string]string{} for _, pi := range pis { importPath := pi.Doc.ImportPath if importPath == mod { + // Add the module root package immediately with the full name. + modTOC.addItem(&tocItem{ + UID: mod, + Name: mod, + Status: pi.Status, + }) continue } if !strings.HasPrefix(importPath, mod) { @@ -576,6 +574,7 @@ func buildTOC(mod string, pis []pkgload.Info, extraFiles []extraFile) tableOfCon } trimmed := strings.TrimPrefix(importPath, mod+"/") trimmedPkgs = append(trimmedPkgs, trimmed) + statuses[trimmed] = pi.Status } sort.Strings(trimmedPkgs) @@ -583,8 +582,9 @@ func buildTOC(mod string, pis []pkgload.Info, extraFiles []extraFile) tableOfCon for _, trimmed := range trimmedPkgs { uid := mod + "/" + trimmed pkgTOCItem := &tocItem{ - UID: uid, - Name: trimmed, + UID: uid, + Name: trimmed, + Status: statuses[trimmed], } modTOC.addItem(pkgTOCItem) } diff --git a/internal/godocfx/pkgload/load.go b/internal/godocfx/pkgload/load.go index 8ffdb9837fb..a706eb3506f 100644 --- a/internal/godocfx/pkgload/load.go +++ b/internal/godocfx/pkgload/load.go @@ -34,6 +34,7 @@ type Info struct { Fset *token.FileSet // ImportRenames is a map from package path to local name or "". ImportRenames map[string]string + Status string } // Load parses the given glob and returns info for the matching packages. @@ -157,12 +158,36 @@ func Load(glob, workingDir string, filter []string) ([]Info, error) { Doc: docPkg, Fset: fset, ImportRenames: imports, + Status: pkgStatus(pkgPath, docPkg.Doc), }) } return result, nil } +// pkgStatus returns the status of the given package with the +// given GoDoc. +// +// pkgStatus does not use repo-metadata-full.json because it's +// not available for all modules nor all versions. +func pkgStatus(importPath, doc string) string { + switch { + case strings.Contains(doc, "\nDeprecated:"): + return "deprecated" + case strings.Contains(doc, "This package is in alpha"): + return "alpha" + case strings.Contains(doc, "This package is in beta"): + return "beta" + + case strings.Contains(importPath, "alpha"): + return "alpha" + case strings.Contains(importPath, "beta"): + return "beta" + } + + return "" +} + func hasPrefix(s string, prefixes []string) bool { for _, prefix := range prefixes { if strings.HasPrefix(s, prefix) { diff --git a/internal/godocfx/pkgload/load_test.go b/internal/godocfx/pkgload/load_test.go new file mode 100644 index 00000000000..b8f03573ee1 --- /dev/null +++ b/internal/godocfx/pkgload/load_test.go @@ -0,0 +1,65 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pkgload + +import "testing" + +func TestPkgStatus(t *testing.T) { + tests := []struct { + importPath string + doc string + want string + }{ + { + importPath: "cloud.google.com/go", + want: "", + }, + { + importPath: "cloud.google.com/go/storage/v1alpha1", + want: "alpha", + }, + { + importPath: "cloud.google.com/go/storage/v2beta2", + want: "beta", + }, + { + doc: "NOTE: This package is in beta. It is not stable, and may be subject to changes.", + want: "beta", + }, + { + doc: "NOTE: This package is in alpha. It is not stable, and is likely to change.", + want: "alpha", + }, + { + doc: "Package foo is great\nDeprecated: not anymore", + want: "deprecated", + }, + { + importPath: "cloud.google.com/go/storage/v1alpha1", + doc: "Package foo is great\nDeprecated: not anymore", + want: "deprecated", // Deprecated comes before alpha and beta. + }, + { + importPath: "cloud.google.com/go/storage/v1beta1", + doc: "Package foo is great\nDeprecated: not anymore", + want: "deprecated", // Deprecated comes before alpha and beta. + }, + } + for _, test := range tests { + if got := pkgStatus(test.importPath, test.doc); got != test.want { + t.Errorf("pkgStatus(%q, %q) got %q, want %q", test.importPath, test.doc, got, test.want) + } + } +} diff --git a/internal/godocfx/testdata/golden/toc.yml b/internal/godocfx/testdata/golden/toc.yml index 051e663c432..36a421cf907 100644 --- a/internal/godocfx/testdata/golden/toc.yml +++ b/internal/godocfx/testdata/golden/toc.yml @@ -2,7 +2,7 @@ - uid: cloud.google.com/go/storage name: cloud.google.com/go/storage items: - - uid: cloud.google.com/go/storage - name: cloud.google.com/go/storage - name: README href: pkg-readme.md + - uid: cloud.google.com/go/storage + name: cloud.google.com/go/storage