blob: eef970932f04fe675317de969d086017e4ae1ea9 [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"
"strings"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/postgres"
"golang.org/x/pkgsite/internal/stdlib"
)
// ImportsDetails contains information for a package's imports.
type ImportsDetails struct {
ModulePath string
// ExternalImports is the collection of package imports that are not in
// the Go standard library and are not part of the same module
ExternalImports []string
// InternalImports is an array of packages representing the package's
// imports that are part of the same module.
InternalImports []string
// StdLib is an array of packages representing the package's imports
// that are in the Go standard library.
StdLib []string
}
// fetchImportsDetails fetches imports for the package version specified by
// pkgPath, modulePath and version from the database and returns a ImportsDetails.
func fetchImportsDetails(ctx context.Context, ds internal.DataSource, pkgPath, modulePath, resolvedVersion string) (_ *ImportsDetails, err error) {
u, err := ds.GetUnit(ctx, &internal.UnitMeta{
Path: pkgPath,
ModulePath: modulePath,
Version: resolvedVersion,
}, internal.WithImports)
if err != nil {
return nil, err
}
var externalImports, moduleImports, std []string
for _, p := range u.Imports {
if stdlib.Contains(p) {
std = append(std, p)
} else if strings.HasPrefix(p+"/", modulePath+"/") {
moduleImports = append(moduleImports, p)
} else {
externalImports = append(externalImports, p)
}
}
return &ImportsDetails{
ModulePath: modulePath,
ExternalImports: externalImports,
InternalImports: moduleImports,
StdLib: std,
}, nil
}
// ImportedByDetails contains information for the collection of packages that
// import a given package.
type ImportedByDetails struct {
ModulePath string
// ImportedBy is the collection of packages that import the
// given package and are not part of the same module.
// They are organized into a tree of sections by prefix.
ImportedBy []*Section
Total int // number of packages in ImportedBy
TotalIsExact bool // if false, then there may be more than Total
}
var (
// mainPageImportedByLimit determines whether the main (unit) page displays
// an exact or an approximate number of importers.
mainPageImportedByLimit = 21
// tabImportedByLimit is the maximum number of importers displayed on the imported
// by page.
tabImportedByLimit = 20001
)
// fetchImportedByDetails fetches importers for the package version specified by
// path and version from the database and returns a ImportedByDetails.
func fetchImportedByDetails(ctx context.Context, ds internal.DataSource, pkgPath, modulePath string) (*ImportedByDetails, error) {
db, ok := ds.(*postgres.DB)
if !ok {
// The proxydatasource does not support the imported by page.
return nil, proxydatasourceNotSupportedErr()
}
importedBy, err := db.GetImportedBy(ctx, pkgPath, modulePath, tabImportedByLimit)
if err != nil {
return nil, err
}
importedByCount := len(importedBy)
// If we reached the query limit, then we don't know the total.
// Say so, and show one less than the limit.
// For example, if the limit is 101 and we get 101 results, then we'll
// say there are more than 100, and show the first 100.
totalIsExact := true
if importedByCount == tabImportedByLimit {
importedBy = importedBy[:len(importedBy)-1]
totalIsExact = false
}
sections := Sections(importedBy, nextPrefixAccount)
return &ImportedByDetails{
ModulePath: modulePath,
ImportedBy: sections,
Total: importedByCount,
TotalIsExact: totalIsExact,
}, nil
}