chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/doc"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
examplePattern = regexp.MustCompile(`@example\s+([\s\S]+?)(?:\n\s*\n|$)`)
|
||||
)
|
||||
|
||||
// extractMethodDoc extracts documentation from a method's Go doc comment
|
||||
func extractMethodDoc(method reflect.Method, rcvrType reflect.Type) (description, example string) {
|
||||
// Get the function's source location
|
||||
fn := method.Func
|
||||
if !fn.IsValid() {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
pc := fn.Pointer()
|
||||
if pc == 0 {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Get the source file location
|
||||
funcForPC := runtime.FuncForPC(pc)
|
||||
if funcForPC == nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
file, _ := funcForPC.FileLine(pc)
|
||||
if file == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Parse the source file
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Find the receiver type name (e.g., "Users" from *Users)
|
||||
rcvrTypeName := rcvrType.Name()
|
||||
if rcvrTypeName == "" && rcvrType.Kind() == reflect.Pointer {
|
||||
rcvrTypeName = rcvrType.Elem().Name()
|
||||
}
|
||||
|
||||
// Search for the method in the AST
|
||||
for _, decl := range f.Decls {
|
||||
funcDecl, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this is a method (has receiver)
|
||||
if funcDecl.Recv == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if method name matches
|
||||
if funcDecl.Name.Name != method.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if receiver type matches
|
||||
if len(funcDecl.Recv.List) > 0 {
|
||||
recvTypeName := getTypeName(funcDecl.Recv.List[0].Type)
|
||||
if recvTypeName != rcvrTypeName {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Found the method! Extract its doc comment
|
||||
if funcDecl.Doc != nil {
|
||||
comment := funcDecl.Doc.Text()
|
||||
return parseComment(comment)
|
||||
}
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// getTypeName extracts the type name from an AST expression
|
||||
func getTypeName(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.StarExpr:
|
||||
return getTypeName(t.X)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// parseComment extracts description and example from a doc comment
|
||||
func parseComment(comment string) (description, example string) {
|
||||
// Extract @example if present
|
||||
if match := examplePattern.FindStringSubmatch(comment); len(match) > 1 {
|
||||
example = strings.TrimSpace(match[1])
|
||||
// Remove @example section from description
|
||||
comment = examplePattern.ReplaceAllString(comment, "")
|
||||
}
|
||||
|
||||
// Clean up the description
|
||||
description = strings.TrimSpace(comment)
|
||||
|
||||
// Use doc.Synopsis for the first sentence if description is long
|
||||
if len(description) > 200 {
|
||||
synopsis := doc.Synopsis(description)
|
||||
if synopsis != "" {
|
||||
description = synopsis
|
||||
}
|
||||
}
|
||||
|
||||
return description, example
|
||||
}
|
||||
|
||||
// extractHandlerDocs extracts documentation for all methods of a handler
|
||||
func extractHandlerDocs(handler interface{}) map[string]map[string]string {
|
||||
metadata := make(map[string]map[string]string)
|
||||
|
||||
typ := reflect.TypeOf(handler)
|
||||
if typ == nil {
|
||||
return metadata
|
||||
}
|
||||
|
||||
// Get the receiver type for methods
|
||||
rcvrType := typ
|
||||
if rcvrType.Kind() == reflect.Pointer {
|
||||
rcvrType = rcvrType.Elem()
|
||||
}
|
||||
|
||||
// Iterate through methods
|
||||
for i := 0; i < typ.NumMethod(); i++ {
|
||||
method := typ.Method(i)
|
||||
|
||||
// Skip non-exported methods
|
||||
if method.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract documentation from source
|
||||
description, example := extractMethodDoc(method, rcvrType)
|
||||
|
||||
if description != "" || example != "" {
|
||||
metadata[method.Name] = make(map[string]string)
|
||||
if description != "" {
|
||||
metadata[method.Name]["description"] = description
|
||||
}
|
||||
if example != "" {
|
||||
metadata[method.Name]["example"] = example
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metadata
|
||||
}
|
||||
Reference in New Issue
Block a user