Skip to content

Commit

Permalink
support define option http method in tag string when field type is Any
Browse files Browse the repository at this point in the history
  • Loading branch information
alimy committed Apr 6, 2022
1 parent 2f76f94 commit 7a6d2b4
Show file tree
Hide file tree
Showing 75 changed files with 1,993 additions and 208 deletions.
36 changes: 36 additions & 0 deletions core/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"errors"
"fmt"
"strings"

"github.com/alimy/mir/v2"
"github.com/alimy/mir/v2/internal/utils"
)

// EngineInfo Engine information
Expand Down Expand Up @@ -97,3 +100,36 @@ func (d Descriptors) keyFrom(s string) string {
func (d *IfaceDescriptor) SetPkgName(name string) {
d.PkgName = name
}

// NotHttpAny not just http any method
func (f *FieldDescriptor) NotHttpAny() bool {
return !strings.HasPrefix(f.HttpMethod, mir.MethodAny)
}

// JustHttpAny not just http any method
func (f *FieldDescriptor) JustHttpAny() bool {
return f.HttpMethod == mir.MethodAny
}

// AnyHttpMethods return methods in HttpMethods
// Note this is assumed HttpMethods like ANY:POST,GET,HEAD
func (f *FieldDescriptor) AnyHttpMethods() []string {
methods := strings.Split(f.HttpMethod, ":")
if len(methods) > 1 {
return strings.Split(methods[1], ",")
}
return nil
}

// HttpMethodArgs return http method as argument like "POST","GET","HEAD"
// Note this is assumed HttpMethods like ANY:POST,GET,HEAD
func (f *FieldDescriptor) HttpMethodArgs() string {
httpMthods := mir.HttpMethods
if strings.HasPrefix(f.HttpMethod, mir.MethodAny) {
methods := strings.Split(f.HttpMethod, ":")
if len(methods) > 1 {
httpMthods = strings.Split(methods[1], ",")
}
}
return utils.QuoteJoin(httpMthods, ",")
}
8 changes: 6 additions & 2 deletions core/descriptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

package core

import "testing"
import (
"testing"

"github.com/alimy/mir/v2"
)

func TestDescriptors(t *testing.T) {
d := make(Descriptors)
Expand All @@ -19,7 +23,7 @@ func TestDescriptors(t *testing.T) {
Host: "",
Path: "/",
Queries: nil,
HttpMethod: "GET",
HttpMethod: mir.MethodGet,
MethodName: "Index",
Comment: "",
},
Expand Down
20 changes: 10 additions & 10 deletions internal/generator/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (
"strings"
"text/template"

"github.com/alimy/mir/v2"
"github.com/alimy/mir/v2/core"
"github.com/alimy/mir/v2/internal/utils"
)

//go:embed templates
Expand Down Expand Up @@ -40,11 +40,11 @@ func templateFrom(name string) (*template.Template, error) {
return nil, err
}
t := template.New("mir").Funcs(template.FuncMap{
"notEmptyStr": notEmptyStr,
"notHttpAny": notHttpAny,
"joinPath": joinPath,
"valideQuery": valideQuery,
"inflateQuery": inflateQuery,
"notEmptyStr": notEmptyStr,
"joinPath": joinPath,
"valideQuery": valideQuery,
"inflateQuery": inflateQuery,
"upperFirstName": upperFirstName,
})
if tmpl, err := t.Parse(string(data)); err == nil {
return tmpl, nil
Expand All @@ -57,10 +57,6 @@ func notEmptyStr(s string) bool {
return s != ""
}

func notHttpAny(m string) bool {
return m != mir.MethodAny
}

func joinPath(group, subpath string) string {
if group == "" {
return subpath
Expand Down Expand Up @@ -93,3 +89,7 @@ func inflateQuery(qs []string) string {
}
return strings.TrimRight(b.String(), ",")
}

func upperFirstName(name string) string {
return utils.UpperFirst(strings.ToLower(name))
}
5 changes: 3 additions & 2 deletions internal/generator/templates/chi_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ func Register{{.TypeName}}Servant(r chi.Router, s {{.TypeName}}) {
router.Use(middlewares...)
{{end}}
// register routes info to router
{{range .Fields}}{{if notHttpAny .HttpMethod }} router.MethodFunc("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else}} router.Head("{{.Path}}", s.{{.MethodName}})
{{range .Fields}}{{if .NotHttpAny}} router.MethodFunc("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} router.Head("{{.Path}}", s.{{.MethodName}})
router.Get("{{.Path}}", s.{{.MethodName}})
router.Post("{{.Path}}", s.{{.MethodName}})
router.Put("{{.Path}}", s.{{.MethodName}})
router.Delete("{{.Path}}", s.{{.MethodName}})
router.Patch("{{.Path}}", s.{{.MethodName}})
router.Options("{{.Path}}", s.{{.MethodName}})
router.Connect("{{.Path}}", s.{{.MethodName}})
router.Trace("{{.Path}}", s.{{.MethodName}}){{end}}
router.Trace("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} router.MethodFunc("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
3 changes: 2 additions & 1 deletion internal/generator/templates/echo_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ func Register{{.TypeName}}Servant(e *echo.Echo, s {{.TypeName}}) {
g.Use(middlewares...)
{{end}}
// register routes info to router
{{range .Fields}}{{if notHttpAny .HttpMethod }} g.Add("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else}} g.Any("{{.Path}}", s.{{.MethodName}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} g.Add("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} g.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} g.Add("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
3 changes: 2 additions & 1 deletion internal/generator/templates/fiber_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func Register{{.TypeName}}Servant(app *fiber.App, s {{.TypeName}}) {
router.Use(middlewares...)
{{end}}
// register routes info to router
{{range .Fields}}{{if eq .HttpMethod "GET" }} router.Get("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "POST"}} router.Post("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "PUT"}} router.Put("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "DELETE"}} router.Delete("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "HEAD"}} router.Head("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "OPTIONS"}} router.Options("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "TRACE"}} router.Trace("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "PATCH"}} router.Patch("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "CONNECT"}} router.Connect("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "ANY" }} router.All("{{.Path}}", s.{{.MethodName}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} router.Add("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} router.All("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} router.Add("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
3 changes: 2 additions & 1 deletion internal/generator/templates/fiber_iface_v2.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func Register{{.TypeName}}Servant(app *fiber.App, s {{.TypeName}}) {
router.Use(middlewares...)
{{end}}
// register routes info to router
{{range .Fields}}{{if eq .HttpMethod "GET" }} router.Get("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "POST"}} router.Post("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "PUT"}} router.Put("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "DELETE"}} router.Delete("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "HEAD"}} router.Head("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "OPTIONS"}} router.Options("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "TRACE"}} router.Trace("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "PATCH"}} router.Patch("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "CONNECT"}} router.Connect("{{.Path}}", s.{{.MethodName}}){{else if eq .HttpMethod "ANY" }} router.All("{{.Path}}", s.{{.MethodName}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} router.Add("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} router.All("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} router.Add("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
3 changes: 2 additions & 1 deletion internal/generator/templates/gin_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ func Register{{.TypeName}}Servant(e *gin.Engine, s {{.TypeName}}) {
router.Use(middlewares...)
{{end}}
// register routes info to router
{{range .Fields}}{{if notHttpAny .HttpMethod }} router.Handle("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else}} router.Any("{{.Path}}", s.{{.MethodName}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} router.Handle("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} router.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} router.Handle("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
5 changes: 3 additions & 2 deletions internal/generator/templates/httprouter_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ type {{.TypeName}} interface {

// Register{{.TypeName}}Servant register {{.TypeName}} servant to httprouter
func Register{{.TypeName}}Servant(r *httprouter.Router, s {{.TypeName}}) {
{{range .Fields}}{{if notHttpAny .HttpMethod }} r.Handle("{{.HttpMethod}}", "{{joinPath $.Group .Path}}", s.{{.MethodName}}){{else}} r.Handle(http.MethodGet, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
{{range .Fields}}{{if .NotHttpAny }} r.Handle("{{.HttpMethod}}", "{{joinPath $.Group .Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} r.Handle(http.MethodGet, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodPut, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodPost, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodDelete, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodHead, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodPatch, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodOptions, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodConnect, "{{joinPath $.Group .Path}}", s.{{.MethodName}})
r.Handle(http.MethodTrace, "{{joinPath $.Group .Path}}", s.{{.MethodName}}){{end}}
r.Handle(http.MethodTrace, "{{joinPath $.Group .Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} r.Handle("{{.}}", "{{joinPath $.Group $field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
3 changes: 2 additions & 1 deletion internal/generator/templates/iris_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ func Register{{.TypeName}}Servant(app *iris.Application, s {{.TypeName}}) {
p.Use(middlewares...)
{{end}}
// register routes info to party
{{range .Fields}}{{if notHttpAny .HttpMethod }} p.Handle("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else}} p.Any("{{.Path}}", s.{{.MethodName}}){{end}}
{{range .Fields}}{{if .NotHttpAny}} p.Handle("{{.HttpMethod}}", "{{.Path}}", s.{{.MethodName}}){{else if .JustHttpAny}} p.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} p.Handle("{{.}}", "{{$field.Path}}", s.{{$field.MethodName}})
{{end}}{{end}}
{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
9 changes: 6 additions & 3 deletions internal/generator/templates/macaron_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@ func Register{{.TypeName}}Servant(m *macaron.Macaron, s {{.TypeName}}) {
{{if notEmptyStr .Group }}{{if notEmptyStr .Chain }} // use chain for router
middlewares := s.{{.Chain}}()
m.Group("{{.Group}}", func() {
{{range .Fields}}{{if notHttpAny .HttpMethod }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else}} m.Any("{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else if .JustHttpAny}} m.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} m.Handle("{{.}}", "{{$field.Path}}", []macaron.Handler{s.{{$field.MethodName}}})
{{end}}{{end}}
{{end}}}, middlewares...){{else}} m.Group("{{.Group}}", func() {
{{range .Fields}}{{if notHttpAny .HttpMethod }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else}} m.Any("{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else if .JustHttpAny}} m.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} m.Handle("{{.}}", "{{$field.Path}}", []macaron.Handler{s.{{$field.MethodName}}})
{{end}}{{end}}
{{end}}}){{end}}{{else}}{{if notEmptyStr .Chain }} // use chain for router
middlewares := s.{{.Chain}}()
for _, middleware := range middlewares {
m.Use(middleware)
}{{end}}
{{range .Fields}}{{if notHttpAny .HttpMethod }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else}} m.Any("{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{end}}
{{range .Fields}}{{if .NotHttpAny }} m.Handle("{{.HttpMethod}}", "{{.Path}}", []macaron.Handler{s.{{.MethodName}}}){{else if .JustHttpAny}} m.Any("{{.Path}}", s.{{.MethodName}}){{else}}{{$field := .}}{{range .AnyHttpMethods}} m.Handle("{{.}}", "{{$field.Path}}", []macaron.Handler{s.{{$field.MethodName}}})
{{end}}{{end}}
{{end}}{{end}}}

{{ $unimplementedServant := print "Unimplemented" .TypeName "Servant" }}
Expand Down
9 changes: 2 additions & 7 deletions internal/generator/templates/mux_iface.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ package {{ .PkgName }}
import (
"net/http"

"github.com/alimy/mir/v2"

mux "{{if notEmptyStr .EngineInfo.PkgName }}{{ .EngineInfo.PkgName }}{{else}}github.com/gorilla/mux{{end}}"
)

// just use for mir.Any method to register
var httpMethods = mir.HttpMethods

{{if notEmptyStr .Comment }}// {{.Comment}}{{end}}
type {{.TypeName}} interface {
{{if notEmptyStr .Chain }} // Chain provide middlewares for mux
Expand All @@ -35,11 +30,11 @@ func Register{{.TypeName}}Servant(r *mux.Router, s {{.TypeName}}) {
{{end}}

// register routes info to router
{{range .Fields}}{{if notHttpAny .HttpMethod }} router.HandleFunc("{{.Path}}", s.{{.MethodName}}).
{{range .Fields}}{{if .NotHttpAny }} router.HandleFunc("{{.Path}}", s.{{.MethodName}}).
Methods("{{.HttpMethod}}"){{if notEmptyStr .Host}}.
Host("{{.Host}}"){{end}}{{if valideQuery .Queries}}.
Queries({{inflateQuery .Queries}}){{end}}{{else}} router.HandleFunc("{{.Path}}", s.{{.MethodName}}).
Methods(httpMethods...){{if notEmptyStr .Host}}.
Methods({{.HttpMethodArgs}}){{if notEmptyStr .Host}}.
Host("{{.Host}}"){{end}}{{if valideQuery .Queries}}.
Queries({{inflateQuery .Queries}}){{end}}{{end}}
{{end}}}
Expand Down
56 changes: 41 additions & 15 deletions internal/parser/fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
package parser

import (
"errors"
"net/http"
"reflect"
"strings"
"unicode"
"unicode/utf8"

"github.com/alimy/mir/v2"
"github.com/alimy/mir/v2/internal/utils"
)

var (
Expand All @@ -23,7 +24,8 @@ var (
errMultChainInfo tagError = "multiple chain info in struct defined"

// defaultTag indicate default mir's struct tag name
defaultTag = "mir"
defaultTag = "mir"
defautlMethodTag = "method"
)

// tagError indicate error information
Expand All @@ -36,7 +38,7 @@ func (e tagError) Error() string {

// tagInfo indicate mir tag information in struct tag string
type tagInfo struct {
Method string // Method indicate method information in struct tag string
Method string // Method indicate methods information in struct tag string
Host string // Host indicate host information in struct tag string
Path string // Path indicate path information in struct tag string
Queries []string // Queries indicate path information in struct tag string
Expand Down Expand Up @@ -95,7 +97,15 @@ func (r *reflex) tagInfoFrom(field reflect.StructField) (*tagInfo, error) {
case "Connect":
info.Method = mir.MethodConnect
case "Any":
info.Method = "ANY"
if methodTag, exist := field.Tag.Lookup(defautlMethodTag); exist {
if methods, ok := r.anyMethodsFromTag(methodTag); ok {
info.Method = mir.MethodAny + ":" + strings.Join(methods, ",")
break
}
}
info.Method = mir.MethodAny
default:
return nil, errors.New("not supported filed type")
}

// host info
Expand Down Expand Up @@ -156,22 +166,38 @@ func (r *reflex) tagInfoFrom(field reflect.StructField) (*tagInfo, error) {

// check handler if not group field
if info.handler == "" {
firstRune, size := utf8.DecodeRuneInString(field.Name)
upperFirst := unicode.ToUpper(firstRune)

// encode upperFirst to []byte,use max byte for contain unicode
methoName := make([]byte, 4)
number := utf8.EncodeRune(methoName, upperFirst)
methoName = methoName[:number]
methoName = append(methoName, field.Name[size:]...)

// assign handler name
info.handler = string(methoName)
info.handler = utils.UpperFirst(field.Name)
}

return info, nil
}

func (r *reflex) anyMethodsFromTag(value string) ([]string, bool) {
anyMethod := strings.TrimSpace(value)
methods := strings.Split(anyMethod, ",")
res := make([]string, 0, len(methods))
for _, method := range methods {
method = strings.ToUpper(strings.TrimSpace(method))
switch method {
case http.MethodGet,
http.MethodHead,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodConnect,
http.MethodOptions,
http.MethodTrace:
res = append(res, method)
}
}
if len(res) > 0 {
return res, true
}
return nil, false
}

func (r *reflex) inflateQuery(qs string) []string {
items := strings.Split(qs, "&")
res := make([]string, 0, len(items)*2)
Expand Down
Loading

0 comments on commit 7a6d2b4

Please sign in to comment.