// 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) }