Skip to content

Commit

Permalink
incorporates some PR changes from s-mang#3. adds customisable handler…
Browse files Browse the repository at this point in the history
…info function for gorilla/mux, using Walk to find the original http handler
  • Loading branch information
cj123 committed Feb 25, 2018
1 parent 17d82d2 commit 198c549
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 65 deletions.
32 changes: 13 additions & 19 deletions doc/parse/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,20 @@ func IsFuncInPkg(longFnName string) bool {
// becomes
// type.method
func getShortFnName(longFnName string) string {
methodRE := regexp.MustCompile(`/(.*)\.(.*)\.(.*)`)
funcRE := regexp.MustCompile(`/(.*)\.(.*)`)

matches := methodRE.FindStringSubmatch(longFnName)
if len(matches) > 0 {
fnName := strings.Join(matches[len(matches)-2:], ".")
fnName = strings.Replace(fnName, "(*", "", -1)
return strings.Replace(fnName, ")", "", -1)
}

// drop anything before the last '/'
slashed := strings.Split(longFnName, "/")
last := slashed[len(slashed)-1]

// split the final part by period
dotted := strings.Split(last, ".")

// drop the first part which is the package name
dotted = dotted[1:]

// loop over and drop pointer references (*v) => v
for i, p := range dotted {
if len(p) > 3 {
if p[0:2] == "(*" && p[len(p)-1] == ')' {
p = p[2 : len(p)-1]
}
}
dotted[i] = p
matches = funcRE.FindStringSubmatch(longFnName)
if len(matches) > 0 {
return matches[len(matches)-1]
}

return strings.Join(dotted, ".")
return ""
}
5 changes: 2 additions & 3 deletions doc/parse/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func NewPackageDoc(dir string) (*doc.Package, error) {
}

setDocFuncsMap(pkgDoc)

return pkgDoc, nil
}

Expand All @@ -49,9 +50,7 @@ func getPkgTypesFunctions(pkgDoc *doc.Package) map[string]*doc.Func {
result := make(map[string]*doc.Func)
for _, t := range pkgDoc.Types {
for _, f := range t.Methods {
if f.Doc != "" {
result[fmt.Sprintf("%s.%s", t.Name, f.Name)] = f
}
result[fmt.Sprintf("%s.%s", t.Name, f.Name)] = f
}
}

Expand Down
80 changes: 80 additions & 0 deletions test/handlerinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package test

import (
"github.com/cj123/test2doc/doc/parse"
"github.com/gorilla/mux"
"log"
"net/http"
"reflect"
"runtime"
"strings"
)

var infoFunc = DefaultHandlerInfo

type HandlerInfo struct {
FuncName string
}

type handlerInfoFunc func(r *http.Request) *HandlerInfo

func RegisterHandlerInfoFunc(fn handlerInfoFunc) {
infoFunc = fn
}

func DefaultHandlerInfo(r *http.Request) *HandlerInfo {
i := 1
max := 15

var pc uintptr
var fnName string
var ok, fnInPkg, sawPkg bool

// iterate until we find the top level func in this pkg (the handler)
for i < max {
pc, _, _, ok = runtime.Caller(i)
if !ok {
log.Println("test2doc: DefaultHandlerInfo: !ok")
return nil
}

fn := runtime.FuncForPC(pc)
fnName = fn.Name()

fnInPkg = parse.IsFuncInPkg(fnName)
if sawPkg && !fnInPkg {
pc, _, _, ok = runtime.Caller(i - 1)
fn := runtime.FuncForPC(pc)
fnName = fn.Name()
break
}

sawPkg = fnInPkg
i++
}

return &HandlerInfo{
FuncName: fnName,
}
}

// GorillaMuxHandlerInfo takes a mux.Router and finds handler info from it.
func GorillaMuxHandlerInfo(router *mux.Router) func(r *http.Request) *HandlerInfo {
return func(r *http.Request) *HandlerInfo {
var handlerInfo *HandlerInfo

router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var match mux.RouteMatch

if route.Match(r, &match) {
fn := runtime.FuncForPC(reflect.ValueOf(match.Handler).Pointer())
handlerInfo = &HandlerInfo{FuncName: strings.Replace(fn.Name(), "-fm", " ", 1)}
return nil
}

return nil
})

return handlerInfo
}
}
47 changes: 7 additions & 40 deletions test/responsewriter.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
package test

import (
"github.com/cj123/test2doc/doc/parse"
"log"
"net/http"
"net/http/httptest"
"runtime"
)

type ResponseWriter struct {
HandlerInfo HandlerInfo
URLVars map[string]string
W *httptest.ResponseRecorder
r *http.Request
}

type HandlerInfo struct {
FileName string
FuncName string
}

func NewResponseWriter(w *httptest.ResponseRecorder) *ResponseWriter {
func NewResponseWriter(w *httptest.ResponseRecorder, r *http.Request) *ResponseWriter {
return &ResponseWriter{
W: w,
r: r,
}
}

Expand All @@ -39,38 +33,11 @@ func (rw *ResponseWriter) WriteHeader(c int) {
}

func (rw *ResponseWriter) setHandlerInfo() {
i := 1
max := 15

var pc uintptr
var file, fnName string
var ok, fnInPkg, sawPkg bool

// iterate until we find the top level func in this pkg (the handler)
for i < max {
pc, file, _, ok = runtime.Caller(i)
if !ok {
log.Println("test2doc: setHandlerInfo: !ok")
return
}
handlerInfo := infoFunc(rw.r)

fn := runtime.FuncForPC(pc)
fnName = fn.Name()

fnInPkg = parse.IsFuncInPkg(fnName)
if sawPkg && !fnInPkg {
pc, file, _, ok = runtime.Caller(i - 1)
fn := runtime.FuncForPC(pc)
fnName = fn.Name()
break
}

sawPkg = fnInPkg
i++
if handlerInfo == nil {
return
}

rw.HandlerInfo = HandlerInfo{
FileName: file,
FuncName: fnName,
}
rw.HandlerInfo = *handlerInfo
}
3 changes: 2 additions & 1 deletion test/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func handleAndRecord(handler http.Handler, outDoc *doc.Doc) http.HandlerFunc {

// record response
rw := httptest.NewRecorder()
resp := NewResponseWriter(rw)
resp := NewResponseWriter(rw, req)

handler.ServeHTTP(resp, req)

Expand All @@ -85,6 +85,7 @@ func handleAndRecord(handler http.Handler, outDoc *doc.Doc) http.HandlerFunc {

// find action
action := resources[path].FindAction(req.Method)

if action == nil {
// make new action
action, err = doc.NewAction(req.Method, resp.HandlerInfo.FuncName)
Expand Down
2 changes: 0 additions & 2 deletions test2doc.go
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
package test2doc


0 comments on commit 198c549

Please sign in to comment.