api-testing/cmd/function.go

145 lines
3.2 KiB
Go

package cmd
import (
"context"
"fmt"
"go/ast"
"go/doc"
"go/parser"
"go/token"
"io"
"path/filepath"
"reflect"
"runtime"
"strings"
"github.com/cucumber/godog"
"github.com/expr-lang/expr/builtin"
"github.com/linuxsuren/api-testing/pkg/render"
"github.com/spf13/cobra"
)
func createFunctionCmd() (c *cobra.Command) {
opt := &funcPrinterOption{}
c = &cobra.Command{
Use: "func",
Short: "Print all the supported functions",
RunE: opt.runE,
}
flags := c.Flags()
flags.StringVarP(&opt.feature, "feature", "", "", "The feature query")
return
}
type funcPrinterOption struct {
feature string
}
func (o *funcPrinterOption) runE(cmd *cobra.Command, args []string) (err error) {
if len(args) > 0 {
name := args[0]
filterAndPrint(cmd, name)
} else if o.feature != "" {
ctx := context.WithValue(cmd.Context(), render.ContextBufferKey, cmd.OutOrStdout())
suite := godog.TestSuite{
ScenarioInitializer: initializeScenario,
Options: &godog.Options{
Format: "pretty",
ShowStepDefinitions: false,
FeatureContents: []godog.Feature{{
Name: "test",
Contents: []byte(fmt.Sprintf(`Feature: title
Scenario: generate
Given %s`, o.feature)),
}},
DefaultContext: ctx,
Output: io.Discard,
},
}
if suite.Run() != 0 {
err = fmt.Errorf("failed to query the feature")
} else {
cmd.Println()
}
} else {
filterAndPrint(cmd, "")
}
return
}
func filterAndPrint(writer printer, name string) {
if name == "" {
writer.Println("All template functions:")
for name, fn := range render.FuncMap() {
writer.Println(name, reflect.TypeOf(fn))
}
} else {
if fn, ok := render.FuncMap()[name]; ok {
writer.Println(reflect.TypeOf(fn))
desc := FuncDescription(fn)
if desc != "" {
writer.Println(desc)
}
} else {
writer.Println("No such function")
}
}
writer.Println("")
writer.Println("All expr functions:")
for _, item := range builtin.Builtins {
if strings.Contains(item.Name, name) {
writer.Println(item.Name, reflect.TypeOf(item.Func))
}
}
}
func initializeScenario(ctx *godog.ScenarioContext) {
funcs := render.GetAdvancedFuncs()
for _, fn := range funcs {
if fn.GoDogExper != "" && fn.Generator != nil {
ctx.Step(fn.GoDogExper, fn.Generator)
}
}
}
// Get the name and path of a func
func FuncPathAndName(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}
// Get the name of a func (with package path)
func FuncName(f interface{}) string {
splitFuncName := strings.Split(FuncPathAndName(f), ".")
return splitFuncName[len(splitFuncName)-1]
}
// Get description of a func
func FuncDescription(f interface{}) (desc string) {
fileName, _ := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).FileLine(0)
funcName := FuncName(f)
fset := token.NewFileSet()
// Parse src
parsedAst, err := parser.ParseFile(fset, fileName, nil, parser.ParseComments)
if err == nil {
pkg := &ast.Package{
Name: "Any",
Files: make(map[string]*ast.File),
}
pkg.Files[fileName] = parsedAst
importPath, _ := filepath.Abs("/")
myDoc := doc.New(pkg, importPath, doc.AllDecls)
for _, theFunc := range myDoc.Funcs {
if theFunc.Name == funcName {
desc = theFunc.Doc
break
}
}
}
return
}