blob: 59b5cc3a66a5f27b71ea1b0fd7228ddf7c70931f [file] [log] [blame]
// Copyright 2024 The Fuchsia 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 bazel_docgen
import (
"path/filepath"
"sort"
"strings"
pb "go.fuchsia.dev/fuchsia/tools/bazel-docgen/third_party/stardoc"
"golang.org/x/exp/maps"
"gopkg.in/yaml.v2"
)
func checkDuplicateName(name string, entries map[string]TOCEntry) bool {
_, ok := entries[name]
return ok
}
func filterEntries(entries []TOCEntry, docType SupportedDocType) []TOCEntry {
var filteredEntries []TOCEntry
for _, entry := range entries {
if entry.DocType == docType {
filteredEntries = append(filteredEntries, entry)
}
}
sort.Slice(filteredEntries, func(i, j int) bool {
return filteredEntries[i].Title < filteredEntries[j].Title
})
return filteredEntries
}
// There are several places in our bazel rules where we wrap a rule with a
// macro. However, these end up as starlark functions in our docs which is not
// what we want. This rule figures out if a starlark function is trying to be a
// macro or an actual function.
func docTypeForStarlarkFunction(f *pb.StarlarkFunctionInfo) SupportedDocType {
params := f.GetParameter()
if len(params) > 0 && params[0].GetName() == "name" {
return DocTypeRule
}
return DocTypeStarlarkFunction
}
type docStringer interface {
GetDocString() string
}
func shortDescription(v docStringer) string {
return strings.Split(v.GetDocString(), "\n")[0]
}
func RenderModuleInfo(roots []pb.ModuleInfo, renderer Renderer, fileProvider FileProvider, basePath string) {
fileProvider.Open()
mkPathFn := func(s string) string {
return filepath.Join("/", basePath, s)
}
entries := make(map[string]TOCEntry)
for _, moduleInfo := range roots {
// Render all of our rules
for _, rule := range moduleInfo.GetRuleInfo() {
fileName := rule.RuleName + ".md"
if checkDuplicateName(fileName, entries) {
continue
}
if err := renderer.RenderRuleInfo(rule, fileProvider.NewFile(fileName)); err != nil {
panic(err)
}
entries[fileName] = NewTOCEntry(rule.RuleName, fileName, shortDescription(rule), DocTypeRule, mkPathFn)
}
// Render all of our providers
for _, provider := range moduleInfo.GetProviderInfo() {
fileName := provider.ProviderName + ".md"
checkDuplicateName(fileName, entries)
if err := renderer.RenderProviderInfo(provider, fileProvider.NewFile(fileName)); err != nil {
panic(err)
}
entries[fileName] = NewTOCEntry(provider.ProviderName, fileName, shortDescription(provider), DocTypeProvider, mkPathFn)
}
// Render all of our starlark functions
for _, funcInfo := range moduleInfo.GetFuncInfo() {
fileName := funcInfo.FunctionName + ".md"
checkDuplicateName(fileName, entries)
if err := renderer.RenderStarlarkFunctionInfo(funcInfo, fileProvider.NewFile(fileName)); err != nil {
panic(err)
}
entries[fileName] = NewTOCEntry(funcInfo.FunctionName, fileName, shortDescription(funcInfo), docTypeForStarlarkFunction(funcInfo), mkPathFn)
}
// Render all of our rules
for _, repoRule := range moduleInfo.GetRepositoryRuleInfo() {
fileName := repoRule.RuleName + ".md"
checkDuplicateName(fileName, entries)
if err := renderer.RenderRepositoryRuleInfo(repoRule, fileProvider.NewFile(fileName)); err != nil {
panic(err)
}
entries[fileName] = NewTOCEntry(repoRule.RuleName, fileName, shortDescription(repoRule), DocTypeRepositoryRule, mkPathFn)
}
}
// Render our README.md
tocEntries := maps.Values(entries)
renderer.RenderReadme(&tocEntries, fileProvider.NewFile("README.md"))
toc := []TOCEntry{
{
Title: "Overview",
Path: mkPathFn("README.md"),
},
{
Title: "API",
Sections: []TOCEntry{
{
Title: "Rules",
Sections: filterEntries(tocEntries, DocTypeRule),
},
{
Title: "Providers",
Sections: filterEntries(tocEntries, DocTypeProvider),
},
{
Title: "Starlark Functions",
Sections: filterEntries(tocEntries, DocTypeStarlarkFunction),
},
{
Title: "Repository Rules",
Sections: filterEntries(tocEntries, DocTypeRepositoryRule),
},
},
},
}
// Make our table of contents
if yamlData, err := yaml.Marshal(&map[string]interface{}{"toc": toc}); err == nil {
tocWriter := fileProvider.NewFile("_toc.yaml")
tocWriter.Write(yamlData)
}
fileProvider.Close()
}