Simplified

This commit is contained in:
2021-10-03 11:39:42 -03:00
parent 5893ad898b
commit 984592b7fd
14 changed files with 784 additions and 199 deletions
+81 -101
View File
@@ -1,6 +1,7 @@
package lib
import (
"errors"
"fmt"
"github.com/fatih/structtag"
"go/ast"
@@ -117,118 +118,89 @@ func addStruct(a *ast.GenDecl) {
api.Types[tp.Name] = &tp
}
func addFunction(a *ast.FuncDecl) {
func addFunction(a *ast.FuncDecl) error {
md := manageComments(a.Doc)
if md["API"] == "" {
return
return nil
}
llog("Adding Fuction: %s => %#v", a.Name, md)
reqType := &APIParamType{}
resType := &APIParamType{}
if len(a.Type.Params.List) > 1 {
switch x := a.Type.Params.List[1].Type.(type) {
case *ast.StarExpr:
reqType.Ispointer = true
switch y := x.X.(type) {
case *ast.Ident:
reqType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
}
case *ast.ArrayType:
reqType.IsArray = true
switch y := x.Elt.(type) {
case *ast.Ident:
reqType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
case *ast.StarExpr:
reqType.Ispointer = true
switch z := y.X.(type) {
case *ast.Ident:
reqType.Typename = z.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = api.Imports[z.X.(*ast.Ident).Name]
reqType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
}
}
case *ast.Ident:
reqType.Typename = x.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = api.Imports[x.X.(*ast.Ident).Name]
reqType.Typename = x.X.(*ast.Ident).Name + "." + x.Sel.Name
}
if len(a.Type.Params.List) != 2 {
return errors.New(fmt.Sprintf("Function %s does not have 2 IN parameters (context and pointer to req struct)", a.Name.Name))
}
if a.Type.Results != nil && len(a.Type.Results.List) > 0 {
switch x := a.Type.Results.List[0].Type.(type) {
case *ast.StarExpr:
resType.Ispointer = true
switch y := x.X.(type) {
case *ast.Ident:
resType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
}
case *ast.ArrayType:
resType.IsArray = true
switch y := x.Elt.(type) {
case *ast.Ident:
resType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
case *ast.StarExpr:
resType.Ispointer = true
switch z := y.X.(type) {
case *ast.Ident:
resType.Typename = z.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = api.Imports[z.X.(*ast.Ident).Name]
resType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
}
}
case *ast.Ident:
resType.Typename = x.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = api.Imports[x.X.(*ast.Ident).Name]
resType.Typename = x.X.(*ast.Ident).Name + "." + x.Sel.Name
}
if md["RAW"] == "true" {
reqType.Typename = md["REQ"]
resType.Typename = md["RES"]
}
verb := md["VERB"]
if verb == "" {
verb = http.MethodPost
}
fn := APIMethod{
Name: a.Name.Name,
Desc: a.Name.Name,
Verb: verb,
Path: md["PATH"],
Perm: md["PERM"],
ReqType: reqType,
ResType: resType,
}
if fn.Path == "" {
fn.Path = "/" + strings.Replace(strings.ToLower(a.Name.Name), "_", "/", -1)
}
api.Methods[a.Name.Name] = &fn
if len(a.Type.Results.List) != 2 {
return errors.New(fmt.Sprintf("Function %s does not have 2 OUT parameters (pointer to res struct and err)", a.Name.Name))
}
if a.Type.Results.List[1].Type.(*ast.Ident).Name != "error" {
return errors.New(fmt.Sprintf("Function %s does not have error as 2nd OUT parameter", a.Name.Name))
}
p0tp := a.Type.Params.List[0].Type.(*ast.SelectorExpr)
if p0tp.X.(*ast.Ident).Name != "context" && p0tp.Sel.Name != "Context" {
return errors.New(fmt.Sprintf("Function %s, param 1 is not of type context.Context", a.Name))
}
switch x := a.Type.Params.List[1].Type.(type) {
case *ast.StarExpr:
reqType.Ispointer = true
switch y := x.X.(type) {
case *ast.Ident:
reqType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
}
default:
return errors.New(fmt.Sprintf("Function %s does not have 2 IN parameters (context and pointer to req struct)", a.Name.Name))
}
switch x := a.Type.Results.List[0].Type.(type) {
case *ast.StarExpr:
resType.Ispointer = true
switch y := x.X.(type) {
case *ast.Ident:
resType.Typename = y.Name
case *ast.SelectorExpr:
api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name]
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
}
default:
return errors.New(fmt.Sprintf("Function %s does not have 2 OUT parameters (pointer to res struct and err)", a.Name.Name))
}
if md["RAW"] == "true" {
reqType.Typename = md["REQ"]
resType.Typename = md["RES"]
}
verb := md["VERB"]
if verb == "" {
verb = http.MethodPost
}
fn := APIMethod{
Name: a.Name.Name,
Desc: a.Name.Name,
Verb: verb,
Path: md["PATH"],
Perm: md["PERM"],
ReqType: reqType,
ResType: resType,
}
if fn.Path == "" {
fn.Path = "/" + strings.Replace(strings.ToLower(a.Name.Name), "_", "/", -1)
}
api.Methods[a.Name.Name] = &fn
return nil
}
func load(src string) error {
@@ -255,6 +227,11 @@ func load(src string) error {
// Print the AST.
ast.Inspect(v, func(n ast.Node) bool {
//tp := reflect.TypeOf(n)
//if tp != nil {
// log.Printf("Type: %#v", tp.String())
//}
switch x := n.(type) {
case *ast.GenDecl:
@@ -278,7 +255,10 @@ func load(src string) error {
case *ast.File:
manageCommentsGroups(x.Comments)
case *ast.FuncDecl:
addFunction(x)
err = addFunction(x)
if err != nil {
return false
}
llog("Adding fn: %s", x.Name)
case *ast.ValueSpec:
if x.Names[0].Name == "BASEPATH" {
@@ -341,5 +321,5 @@ func load(src string) error {
api.Namespace = packageName
return nil
return err
}
+148 -40
View File
@@ -27,16 +27,117 @@ func processGoServerOutput(f string) error {
WNL("package %s", api.Namespace)
WNL(`import (
"context"
"encoding/json"
"strings"
"net/http"
)`)
"context"
"encoding/json"
"strings"
"net/http"
"errors"
"reflect"
"strconv"
)`)
for k := range api.UsedImportsFunctions {
WNL(`import "%s"`, k)
}
WNL(`func handleTag(s string, r *http.Request) string {
parts := strings.Split(s, ":")
where := "Q"
key := ""
if len(parts) == 1 {
key = parts[0]
} else {
where = parts[0]
key = parts[1]
}
switch where {
case "Q":
return r.URL.Query().Get(key)
case "H":
return r.Header.Get(key)
case "P":
switch key {
case "*":
return r.URL.Path
case "last":
pps := strings.Split(r.URL.Path, "/")
return pps[len(pps)-1]
case "len":
pps := strings.Split(r.URL.Path, "/")
return strconv.Itoa(len(pps))
default:
pps := strings.Split(r.URL.Path, "/")
n, _ := strconv.Atoi(key)
if n < len(pps) {
return pps[n]
}
return ""
}
}
return ""
}
func convert(s string, tpname string) interface{} {
switch tpname {
case "string":
return s
case "int":
v, _ := strconv.Atoi(s)
return v
case "int8":
v, _ := strconv.Atoi(s)
return int8(v)
case "int16":
v, _ := strconv.Atoi(s)
return int16(v)
case "int32":
v, _ := strconv.Atoi(s)
return int32(v)
case "int64":
v, _ := strconv.Atoi(s)
return int64(v)
case "uint":
v, _ := strconv.Atoi(s)
return uint(v)
case "float32":
v, _ := strconv.Atoi(s)
return float32(v)
case "float64":
v, _ := strconv.Atoi(s)
return float64(v)
case "bool":
return s == "true" || s == "1" || s == "Y"
}
return nil
}
func Map(r *http.Request, in interface{}) error {
tp := reflect.TypeOf(in)
vl := reflect.ValueOf(in)
if tp.Kind() == reflect.Ptr {
tp = tp.Elem()
vl = vl.Elem()
}
if tp.Kind() != reflect.Struct {
return errors.New("Type is not struct")
}
for i := 0; i < tp.NumField(); i++ {
k, ok := tp.Field(i).Tag.Lookup("in")
if ok {
str := handleTag(k, r)
v := convert(str, tp.Field(i).Type.Name())
strv := reflect.ValueOf(v)
vl.Field(i).Set(strv)
}
}
return nil
}
`)
WNL(`type API struct {
Mux *http.ServeMux
Perms map[string]string
@@ -47,41 +148,6 @@ func (a *API) GetPerm(r *http.Request) string {
}
`)
WNL(`func Init() *API{
mux := &http.ServeMux{}
ret := &API{
Mux: mux,
Perms: make(map[string]string),
}`)
for _, v := range api.Methods {
if v.Perm != "" {
WNL(` ret.Perms["%s_%s"]="%s"`, v.Verb, v.Path, v.Perm)
}
}
for _, v := range api.SortedPaths {
WNL(` mux.HandleFunc("%s",func(w http.ResponseWriter, r *http.Request) {
switch r.Method {`, v.Path)
for _, v1 := range v.SortedVerbs {
WNL(` case "%s":`, v1.Method.Verb)
if v1.Method.Raw {
WNL(` %s(w,r)`, v1.Method.Name)
} else {
WNL(` h_%s(w,r)`, v1.Method.Name)
}
}
WNL(` default:
http.Error(w,"Method not allowed",500)
}`)
}
WNL(` })`)
WNL(` return ret
}`)
for _, v := range api.Methods {
WNL(`func h_%s(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -90,6 +156,12 @@ func (a *API) GetPerm(r *http.Request) string {
WNL(" req := %s", ResImplType(v.ReqType))
WNL(` err:=Map(r,req)
if err != nil {
http.Error(w, err.Error(), 500)
return
}`)
WNL(` if r.Method!=http.MethodGet && r.Method!=http.MethodHead {`)
if v.ReqType.Ispointer || v.ReqType.IsArray {
@@ -119,5 +191,41 @@ func (a *API) GetPerm(r *http.Request) string {
}
WNL(`func Init() *API{
mux := &http.ServeMux{}
ret := &API{
Mux: mux,
Perms: make(map[string]string),
}`)
for _, v := range api.Methods {
if v.Perm != "" {
WNL(` ret.Perms["%s_%s"]="%s"`, v.Verb, v.Path, v.Perm)
}
}
for _, v := range api.SortedPaths {
WNL(` mux.HandleFunc("%s",func(w http.ResponseWriter, r *http.Request) {
switch r.Method {`, v.Path)
for _, v1 := range v.SortedVerbs {
WNL(` case "%s":`, v1.Method.Verb)
if v1.Method.Raw {
WNL(` %s(w,r)`, v1.Method.Name)
} else {
WNL(` h_%s(w,r)`, v1.Method.Name)
}
}
WNL(` default:
http.Error(w,"Method not allowed",500)
}`)
WNL(` })`)
}
WNL(` return ret
}`)
return os.WriteFile(f, buf.Bytes(), 0600)
}