docreflect/docs.go
2025-08-08 21:44:31 +02:00

152 lines
3.6 KiB
Go

// Package docreflect returns the Go documentation for packages, types and functions.
//
// The Go documentation of packages is not available during runtime by default, so in order to use docreflect, the documentation
// for the selected packages and symbols needs to be generated during build time. To generate the documentation, see the
// docreflect/generate package or the docreflect generate command.
package docreflect
import (
"fmt"
"reflect"
"regexp"
"runtime"
"strings"
)
var (
registry = make(map[string]string)
funcParamsExp = regexp.MustCompile("^func[(]([a-zA-Z_][a-zA-Z0-9_]+(, [a-zA-Z_][a-zA-Z0-9_]+)*)?[)]$")
)
func docs(p string) string {
return registry[p]
}
func functionPath(v reflect.Value) string {
t := v.Type()
pp := t.PkgPath()
p := v.Pointer()
rf := runtime.FuncForPC(p)
n := rf.Name()
// method names appear to have an -fm suffix:
np := strings.Split(n, ".")
npl := np[len(np)-1]
nplp := strings.Split(npl, "-")
if len(nplp) > 1 {
npl = nplp[0]
np[len(np)-1] = npl
n = strings.Join(np, ".")
}
if strings.HasPrefix(n, pp) {
return n
}
// sometimes we get packagename.FunctionName instead of the full import path:
np = strings.Split(n, ".")
if len(np) > 1 {
np = np[1:]
}
return strings.Join(append([]string{pp}, np...), ".")
}
func splitDocs(d string) (string, string) {
parts := strings.Split(d, "\n")
last := len(parts) - 1
lastPart := parts[last]
var params string
if funcParamsExp.MatchString(lastPart) {
parts = parts[:last]
params = lastPart
}
return strings.Join(parts, "\n"), params
}
func functionParams(d string) []string {
_, p := splitDocs(d)
if p == "" {
return nil
}
p = p[len("func(") : len(p)-1]
return strings.Split(p, ", ")
}
// Register is used by the code generated by the docreflect/generate package or the docreflect generate command to register
// the selected documentation during startup. Register is not meant to be used directly by importing packages.
func Register(gopath, docs string) {
registry[gopath] = docs
}
// Docs returns the documentation for a package or a symbol in a package identified by its full go path.
func Docs(gopath string) string {
d := docs(gopath)
d, _ = splitDocs(d)
return d
}
// Function returns the documentation for a package level function.
func Function(v reflect.Value) string {
if v.Kind() != reflect.Func {
return ""
}
return Docs(functionPath(v))
}
// FunctionParams returns the list of the parameter names of a package level function.
func FunctionParams(v reflect.Value) []string {
if v.Kind() != reflect.Func {
return nil
}
d := docs(functionPath(v))
return functionParams(d)
}
// Type returns the docuemntation for a package level type.
func Type(t reflect.Type) string {
p := fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
return docs(p)
}
// Field returns the docuemntation for a struct field.
func Field(t reflect.Type, fieldPath ...string) string {
if len(fieldPath) == 0 {
return ""
}
if t.Kind() != reflect.Struct {
return ""
}
p := strings.Join(append([]string{t.PkgPath(), t.Name()}, fieldPath...), ".")
println(p)
return docs(p)
}
// Method returns the documentation for a type method.
func Method(t reflect.Type, name string) string {
if t.Kind() != reflect.Struct {
return ""
}
p := fmt.Sprintf("%s.%s.%s", t.PkgPath(), t.Name(), name)
return docs(p)
}
// MethodParams returns the list of the parameter names of a type method.
func MethodParams(t reflect.Type, name string) []string {
if t.Kind() != reflect.Struct {
return nil
}
p := fmt.Sprintf("%s.%s.%s", t.PkgPath(), t.Name(), name)
d := docs(p)
return functionParams(d)
}