blob: 4533e2f5f44ba6997290faebf4d854ab3579288b [file] [log] [blame]
// 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 (
"context"
"fmt"
"strings"
"time"
"github.com/google/safehtml"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/experiment"
"golang.org/x/pkgsite/internal/godoc"
"golang.org/x/pkgsite/internal/log"
"golang.org/x/pkgsite/internal/stdlib"
)
// DocumentationDetails contains data for the doc template.
type DocumentationDetails struct {
GOOS string
GOARCH string
Documentation safehtml.HTML
}
// fetchDocumentationDetails returns a DocumentationDetails.
func fetchDocumentationDetails(ctx context.Context, ds internal.DataSource, um *internal.UnitMeta) (_ *DocumentationDetails, err error) {
u, err := ds.GetUnit(ctx, um, internal.WithDocumentation)
if err != nil {
return nil, err
}
if experiment.IsActive(ctx, internal.ExperimentFrontendRenderDoc) && len(u.Documentation.Source) > 0 {
dd, err := renderDoc(ctx, u)
if err != nil {
log.Errorf(ctx, "render doc failed: %v", err)
// Fall through to use stored doc.
} else {
return dd, nil
}
}
return &DocumentationDetails{
GOOS: u.Documentation.GOOS,
GOARCH: u.Documentation.GOARCH,
Documentation: u.Documentation.HTML,
}, nil
}
func renderDoc(ctx context.Context, u *internal.Unit) (*DocumentationDetails, error) {
start := time.Now()
docPkg, err := godoc.DecodePackage(u.Documentation.Source)
if err != nil {
return nil, err
}
modInfo := &godoc.ModuleInfo{
ModulePath: u.ModulePath,
ResolvedVersion: u.Version,
ModulePackages: nil, // will be provided by docPkg
}
var innerPath string
if u.ModulePath == stdlib.ModulePath {
innerPath = u.Path
} else if u.Path != u.ModulePath {
innerPath = u.Path[len(u.ModulePath)+1:]
}
_, _, html, err := docPkg.Render(ctx, innerPath, u.SourceInfo, modInfo, "", "")
if err != nil {
return nil, err
}
log.Infof(ctx, "rendered doc for %s@%s in %s", u.Path, u.Version, time.Since(start))
return &DocumentationDetails{
GOOS: docPkg.GOOS,
GOARCH: docPkg.GOARCH,
Documentation: html,
}, nil
}
// fileSource returns the original filepath in the module zip where the given
// filePath can be found. For std, the corresponding URL in
// go.google.source.com/go is returned.
func fileSource(modulePath, version, filePath string) string {
if modulePath != stdlib.ModulePath {
return fmt.Sprintf("%s@%s/%s", modulePath, version, filePath)
}
root := strings.TrimPrefix(stdlib.GoRepoURL, "https://")
tag, err := stdlib.TagForVersion(version)
if err != nil {
// This should never happen unless there is a bug in
// stdlib.TagForVersion. In which case, fallback to the default
// zipFilePath.
log.Errorf(context.TODO(), "fileSource: %v", err)
return fmt.Sprintf("%s/+/refs/heads/master/%s", root, filePath)
}
return fmt.Sprintf("%s/+/refs/tags/%s/%s", root, tag, filePath)
}