Code optimized (#493)

master
kingxt 4 years ago committed by GitHub
parent 059027bc9d
commit f98c9246b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -39,6 +39,7 @@ service {{.serviceName}} {
} }
` `
// ApiCommand create api template file
func ApiCommand(c *cli.Context) error { func ApiCommand(c *cli.Context) error {
apiFile := c.String("o") apiFile := c.String("o")
if len(apiFile) == 0 { if len(apiFile) == 0 {

@ -9,6 +9,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// DartCommand create dart network request code
func DartCommand(c *cli.Context) error { func DartCommand(c *cli.Context) error {
apiFile := c.String("api") apiFile := c.String("api")
dir := c.String("dir") dir := c.String("dir")

@ -2,9 +2,9 @@ package dartgen
import ( import (
"os" "os"
"reflect"
"strings" "strings"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
) )
@ -32,10 +32,18 @@ func pathToFuncName(path string) string {
return util.ToLower(camel[:1]) + camel[1:] return util.ToLower(camel[:1]) + camel[1:]
} }
func tagGet(tag, k string) (reflect.Value, error) { func tagGet(tag, k string) string {
v, _ := util.TagLookup(tag, k) tags, err := spec.Parse(tag)
out := strings.Split(v, ",")[0] if err != nil {
return reflect.ValueOf(out), nil panic(k + " not exist")
}
v, err := tags.Get(k)
if err != nil {
panic(k + " value not exist")
}
return v.Name
} }
func isDirectType(s string) bool { func isDirectType(s string) bool {

@ -12,6 +12,7 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// DocCommand generate markdown doc file
func DocCommand(c *cli.Context) error { func DocCommand(c *cli.Context) error {
dir := c.String("dir") dir := c.String("dir")
if len(dir) == 0 { if len(dir) == 0 {

@ -160,6 +160,7 @@ type Response struct {
@server( @server(
jwt: Auth jwt: Auth
signature: true
) )
service A-api { service A-api {
@handler GreetHandler @handler GreetHandler

@ -40,7 +40,7 @@ func genConfig(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, item := range authNames { for _, item := range authNames {
auths = append(auths, fmt.Sprintf("%s %s", item, jwtTemplate)) auths = append(auths, fmt.Sprintf("%s %s", item, jwtTemplate))
} }
var authImportStr = fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceUrl) var authImportStr = fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceURL)
return genFile(fileGenConfig{ return genFile(fileGenConfig{
dir: dir, dir: dir,

@ -109,7 +109,7 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
if len(route.RequestTypeName()) > 0 { if len(route.RequestTypeName()) > 0 {
imports = append(imports, fmt.Sprintf("\"%s\"\n", util.JoinPackages(parentPkg, typesDir))) imports = append(imports, fmt.Sprintf("\"%s\"\n", util.JoinPackages(parentPkg, typesDir)))
} }
imports = append(imports, fmt.Sprintf("\"%s/rest/httpx\"", vars.ProjectOpenSourceUrl)) imports = append(imports, fmt.Sprintf("\"%s/rest/httpx\"", vars.ProjectOpenSourceURL))
return strings.Join(imports, "\n\t") return strings.Join(imports, "\n\t")
} }

@ -122,6 +122,6 @@ func genLogicImports(route spec.Route, parentPkg string) string {
if len(route.ResponseTypeName()) > 0 || len(route.RequestTypeName()) > 0 { if len(route.ResponseTypeName()) > 0 || len(route.RequestTypeName()) > 0 {
imports = append(imports, fmt.Sprintf("\"%s\"\n", ctlutil.JoinPackages(parentPkg, typesDir))) imports = append(imports, fmt.Sprintf("\"%s\"\n", ctlutil.JoinPackages(parentPkg, typesDir)))
} }
imports = append(imports, fmt.Sprintf("\"%s/core/logx\"", vars.ProjectOpenSourceUrl)) imports = append(imports, fmt.Sprintf("\"%s/core/logx\"", vars.ProjectOpenSourceURL))
return strings.Join(imports, "\n\t") return strings.Join(imports, "\n\t")
} }

@ -74,7 +74,7 @@ func genMainImports(parentPkg string) string {
imports = append(imports, fmt.Sprintf("\"%s\"", ctlutil.JoinPackages(parentPkg, configDir))) imports = append(imports, fmt.Sprintf("\"%s\"", ctlutil.JoinPackages(parentPkg, configDir)))
imports = append(imports, fmt.Sprintf("\"%s\"", ctlutil.JoinPackages(parentPkg, handlerDir))) imports = append(imports, fmt.Sprintf("\"%s\"", ctlutil.JoinPackages(parentPkg, handlerDir)))
imports = append(imports, fmt.Sprintf("\"%s\"\n", ctlutil.JoinPackages(parentPkg, contextDir))) imports = append(imports, fmt.Sprintf("\"%s\"\n", ctlutil.JoinPackages(parentPkg, contextDir)))
imports = append(imports, fmt.Sprintf("\"%s/core/conf\"", vars.ProjectOpenSourceUrl)) imports = append(imports, fmt.Sprintf("\"%s/core/conf\"", vars.ProjectOpenSourceURL))
imports = append(imports, fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceUrl)) imports = append(imports, fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceURL))
return strings.Join(imports, "\n\t") return strings.Join(imports, "\n\t")
} }

@ -89,7 +89,7 @@ func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
} }
var signature string var signature string
if g.signatureEnabled { if g.signatureEnabled {
signature = fmt.Sprintf("\n rest.WithSignature(serverCtx.Config.%s.Signature),", g.authName) signature = "\n rest.WithSignature(serverCtx.Config.Signature),"
} }
var routes string var routes string
@ -163,7 +163,7 @@ func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
imports := importSet.KeysStr() imports := importSet.KeysStr()
sort.Strings(imports) sort.Strings(imports)
projectSection := strings.Join(imports, "\n\t") projectSection := strings.Join(imports, "\n\t")
depSection := fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceUrl) depSection := fmt.Sprintf("\"%s/rest\"", vars.ProjectOpenSourceURL)
return fmt.Sprintf("%s\n\n\t%s", projectSection, depSection) return fmt.Sprintf("%s\n\n\t%s", projectSection, depSection)
} }
@ -196,6 +196,10 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
groupedRoutes.authName = jwt groupedRoutes.authName = jwt
groupedRoutes.jwtEnabled = true groupedRoutes.jwtEnabled = true
} }
signature := g.GetAnnotation("signature")
if signature == "true" {
groupedRoutes.signatureEnabled = true
}
middleware := g.GetAnnotation("middleware") middleware := g.GetAnnotation("middleware")
if len(middleware) > 0 { if len(middleware) > 0 {
for _, item := range strings.Split(middleware, ",") { for _, item := range strings.Split(middleware, ",") {

@ -64,7 +64,7 @@ func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error
var configImport = "\"" + ctlutil.JoinPackages(parentPkg, configDir) + "\"" var configImport = "\"" + ctlutil.JoinPackages(parentPkg, configDir) + "\""
if len(middlewareStr) > 0 { if len(middlewareStr) > 0 {
configImport += "\n\t\"" + ctlutil.JoinPackages(parentPkg, middlewareDir) + "\"" configImport += "\n\t\"" + ctlutil.JoinPackages(parentPkg, middlewareDir) + "\""
configImport += fmt.Sprintf("\n\t\"%s/rest\"", vars.ProjectOpenSourceUrl) configImport += fmt.Sprintf("\n\t\"%s/rest\"", vars.ProjectOpenSourceURL)
} }
return genFile(fileGenConfig{ return genFile(fileGenConfig{

@ -94,10 +94,6 @@ func getAuths(api *spec.ApiSpec) []string {
if len(jwt) > 0 { if len(jwt) > 0 {
authNames.Add(jwt) authNames.Add(jwt)
} }
signature := g.GetAnnotation("signature")
if len(signature) > 0 {
authNames.Add(signature)
}
} }
return authNames.KeysStr() return authNames.KeysStr()
} }

@ -22,12 +22,6 @@ type Api struct {
} }
func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} { func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} {
defer func() {
if p := recover(); p != nil {
panic(fmt.Errorf("%+v", p))
}
}()
var final Api var final Api
final.importM = map[string]PlaceHolder{} final.importM = map[string]PlaceHolder{}
final.typeM = map[string]PlaceHolder{} final.typeM = map[string]PlaceHolder{}
@ -36,107 +30,126 @@ func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} {
final.routeM = map[string]PlaceHolder{} final.routeM = map[string]PlaceHolder{}
for _, each := range ctx.AllSpec() { for _, each := range ctx.AllSpec() {
root := each.Accept(v).(*Api) root := each.Accept(v).(*Api)
if root.Syntax != nil { v.acceptSyntax(root, &final)
if final.Syntax != nil { v.accetpImport(root, &final)
v.panic(root.Syntax.Syntax, fmt.Sprintf("mutiple syntax declaration")) v.acceptInfo(root, &final)
} v.acceptType(root, &final)
v.acceptService(root, &final)
final.Syntax = root.Syntax }
}
for _, imp := range root.Import { return &final
if _, ok := final.importM[imp.Value.Text()]; ok { }
v.panic(imp.Import, fmt.Sprintf("duplicate import '%s'", imp.Value.Text()))
}
final.importM[imp.Value.Text()] = Holder func (v *ApiVisitor) acceptService(root *Api, final *Api) {
final.Import = append(final.Import, imp) for _, service := range root.Service {
if _, ok := final.serviceM[service.ServiceApi.Name.Text()]; !ok && len(final.serviceM) > 0 {
v.panic(service.ServiceApi.Name, fmt.Sprintf("mutiple service declaration"))
} }
v.duplicateServerItemCheck(service)
if root.Info != nil { for _, route := range service.ServiceApi.ServiceRoute {
infoM := map[string]PlaceHolder{} uniqueRoute := fmt.Sprintf("%s %s", route.Route.Method.Text(), route.Route.Path.Text())
if final.Info != nil { if _, ok := final.routeM[uniqueRoute]; ok {
v.panic(root.Info.Info, fmt.Sprintf("mutiple info declaration")) v.panic(route.Route.Method, fmt.Sprintf("duplicate route '%s'", uniqueRoute))
} }
for _, value := range root.Info.Kvs { final.routeM[uniqueRoute] = Holder
if _, ok := infoM[value.Key.Text()]; ok { var handlerExpr Expr
v.panic(value.Key, fmt.Sprintf("duplicate key '%s'", value.Key.Text())) if route.AtServer != nil {
atServerM := map[string]PlaceHolder{}
for _, kv := range route.AtServer.Kv {
if _, ok := atServerM[kv.Key.Text()]; ok {
v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text()))
}
atServerM[kv.Key.Text()] = Holder
if kv.Key.Text() == "handler" {
handlerExpr = kv.Value
}
} }
infoM[value.Key.Text()] = Holder
} }
final.Info = root.Info if route.AtHandler != nil {
} handlerExpr = route.AtHandler.Name
for _, tp := range root.Type {
if _, ok := final.typeM[tp.NameExpr().Text()]; ok {
v.panic(tp.NameExpr(), fmt.Sprintf("duplicate type '%s'", tp.NameExpr().Text()))
} }
final.typeM[tp.NameExpr().Text()] = Holder if handlerExpr == nil {
final.Type = append(final.Type, tp) v.panic(route.Route.Method, fmt.Sprintf("mismtached handler"))
}
for _, service := range root.Service {
if _, ok := final.serviceM[service.ServiceApi.Name.Text()]; !ok && len(final.serviceM) > 0 {
v.panic(service.ServiceApi.Name, fmt.Sprintf("mutiple service declaration"))
} }
if service.AtServer != nil { if handlerExpr.Text() == "" {
atServerM := map[string]PlaceHolder{} v.panic(handlerExpr, fmt.Sprintf("mismtached handler"))
for _, kv := range service.AtServer.Kv { }
if _, ok := atServerM[kv.Key.Text()]; ok {
v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text()))
}
atServerM[kv.Key.Text()] = Holder if _, ok := final.handlerM[handlerExpr.Text()]; ok {
} v.panic(handlerExpr, fmt.Sprintf("duplicate handler '%s'", handlerExpr.Text()))
} }
final.handlerM[handlerExpr.Text()] = Holder
}
final.Service = append(final.Service, service)
}
}
for _, route := range service.ServiceApi.ServiceRoute { func (v *ApiVisitor) duplicateServerItemCheck(service *Service) {
uniqueRoute := fmt.Sprintf("%s %s", route.Route.Method.Text(), route.Route.Path.Text()) if service.AtServer != nil {
if _, ok := final.routeM[uniqueRoute]; ok { atServerM := map[string]PlaceHolder{}
v.panic(route.Route.Method, fmt.Sprintf("duplicate route '%s'", uniqueRoute)) for _, kv := range service.AtServer.Kv {
} if _, ok := atServerM[kv.Key.Text()]; ok {
v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text()))
}
final.routeM[uniqueRoute] = Holder atServerM[kv.Key.Text()] = Holder
var handlerExpr Expr }
if route.AtServer != nil { }
atServerM := map[string]PlaceHolder{} }
for _, kv := range route.AtServer.Kv {
if _, ok := atServerM[kv.Key.Text()]; ok {
v.panic(kv.Key, fmt.Sprintf("duplicate key '%s'", kv.Key.Text()))
}
atServerM[kv.Key.Text()] = Holder
if kv.Key.Text() == "handler" {
handlerExpr = kv.Value
}
}
}
if route.AtHandler != nil { func (v *ApiVisitor) acceptType(root *Api, final *Api) {
handlerExpr = route.AtHandler.Name for _, tp := range root.Type {
} if _, ok := final.typeM[tp.NameExpr().Text()]; ok {
v.panic(tp.NameExpr(), fmt.Sprintf("duplicate type '%s'", tp.NameExpr().Text()))
}
if handlerExpr == nil { final.typeM[tp.NameExpr().Text()] = Holder
v.panic(route.Route.Method, fmt.Sprintf("mismtached handler")) final.Type = append(final.Type, tp)
} }
}
if handlerExpr.Text() == "" { func (v *ApiVisitor) acceptInfo(root *Api, final *Api) {
v.panic(handlerExpr, fmt.Sprintf("mismtached handler")) if root.Info != nil {
} infoM := map[string]PlaceHolder{}
if final.Info != nil {
v.panic(root.Info.Info, fmt.Sprintf("mutiple info declaration"))
}
if _, ok := final.handlerM[handlerExpr.Text()]; ok { for _, value := range root.Info.Kvs {
v.panic(handlerExpr, fmt.Sprintf("duplicate handler '%s'", handlerExpr.Text())) if _, ok := infoM[value.Key.Text()]; ok {
} v.panic(value.Key, fmt.Sprintf("duplicate key '%s'", value.Key.Text()))
final.handlerM[handlerExpr.Text()] = Holder
} }
final.Service = append(final.Service, service) infoM[value.Key.Text()] = Holder
} }
final.Info = root.Info
} }
}
return &final func (v *ApiVisitor) accetpImport(root *Api, final *Api) {
for _, imp := range root.Import {
if _, ok := final.importM[imp.Value.Text()]; ok {
v.panic(imp.Import, fmt.Sprintf("duplicate import '%s'", imp.Value.Text()))
}
final.importM[imp.Value.Text()] = Holder
final.Import = append(final.Import, imp)
}
}
func (v *ApiVisitor) acceptSyntax(root *Api, final *Api) {
if root.Syntax != nil {
if final.Syntax != nil {
v.panic(root.Syntax.Syntax, fmt.Sprintf("mutiple syntax declaration"))
}
final.Syntax = root.Syntax
}
} }
func (v *ApiVisitor) VisitSpec(ctx *api.SpecContext) interface{} { func (v *ApiVisitor) VisitSpec(ctx *api.SpecContext) interface{} {

@ -156,28 +156,9 @@ func (p *Parser) invoke(linePrefix, content string) (v *Api, err error) {
} }
func (p *Parser) valid(mainApi *Api, nestedApi *Api) error { func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
if len(nestedApi.Import) > 0 { err := p.nestedApiCheck(mainApi, nestedApi)
importToken := nestedApi.Import[0].Import if err != nil {
return fmt.Errorf("%s line %d:%d the nested api does not support import", return err
nestedApi.LinePrefix, importToken.Line(), importToken.Column())
}
if mainApi.Syntax != nil && nestedApi.Syntax != nil {
if mainApi.Syntax.Version.Text() != nestedApi.Syntax.Version.Text() {
syntaxToken := nestedApi.Syntax.Syntax
return fmt.Errorf("%s line %d:%d multiple syntax declaration, expecting syntax '%s', but found '%s'",
nestedApi.LinePrefix, syntaxToken.Line(), syntaxToken.Column(), mainApi.Syntax.Version.Text(), nestedApi.Syntax.Version.Text())
}
}
if len(mainApi.Service) > 0 {
mainService := mainApi.Service[0]
for _, service := range nestedApi.Service {
if mainService.ServiceApi.Name.Text() != service.ServiceApi.Name.Text() {
return fmt.Errorf("%s multiple service name declaration, expecting service name '%s', but found '%s'",
nestedApi.LinePrefix, mainService.ServiceApi.Name.Text(), service.ServiceApi.Name.Text())
}
}
} }
mainHandlerMap := make(map[string]PlaceHolder) mainHandlerMap := make(map[string]PlaceHolder)
@ -218,6 +199,23 @@ func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
} }
// duplicate route check // duplicate route check
err = p.duplicateRouteCheck(nestedApi, mainHandlerMap, mainRouteMap)
if err != nil {
return err
}
// duplicate type check
for _, each := range nestedApi.Type {
if _, ok := mainTypeMap[each.NameExpr().Text()]; ok {
return fmt.Errorf("%s line %d:%d duplicate type declaration '%s'",
nestedApi.LinePrefix, each.NameExpr().Line(), each.NameExpr().Column(), each.NameExpr().Text())
}
}
return nil
}
func (p *Parser) duplicateRouteCheck(nestedApi *Api, mainHandlerMap map[string]PlaceHolder, mainRouteMap map[string]PlaceHolder) error {
for _, each := range nestedApi.Service { for _, each := range nestedApi.Service {
for _, r := range each.ServiceApi.ServiceRoute { for _, r := range each.ServiceApi.ServiceRoute {
handler := r.GetHandler() handler := r.GetHandler()
@ -237,12 +235,31 @@ func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
} }
} }
} }
return nil
}
// duplicate type check func (p *Parser) nestedApiCheck(mainApi *Api, nestedApi *Api) error {
for _, each := range nestedApi.Type { if len(nestedApi.Import) > 0 {
if _, ok := mainTypeMap[each.NameExpr().Text()]; ok { importToken := nestedApi.Import[0].Import
return fmt.Errorf("%s line %d:%d duplicate type declaration '%s'", return fmt.Errorf("%s line %d:%d the nested api does not support import",
nestedApi.LinePrefix, each.NameExpr().Line(), each.NameExpr().Column(), each.NameExpr().Text()) nestedApi.LinePrefix, importToken.Line(), importToken.Column())
}
if mainApi.Syntax != nil && nestedApi.Syntax != nil {
if mainApi.Syntax.Version.Text() != nestedApi.Syntax.Version.Text() {
syntaxToken := nestedApi.Syntax.Syntax
return fmt.Errorf("%s line %d:%d multiple syntax declaration, expecting syntax '%s', but found '%s'",
nestedApi.LinePrefix, syntaxToken.Line(), syntaxToken.Column(), mainApi.Syntax.Version.Text(), nestedApi.Syntax.Version.Text())
}
}
if len(mainApi.Service) > 0 {
mainService := mainApi.Service[0]
for _, service := range nestedApi.Service {
if mainService.ServiceApi.Name.Text() != service.ServiceApi.Name.Text() {
return fmt.Errorf("%s multiple service name declaration, expecting service name '%s', but found '%s'",
nestedApi.LinePrefix, mainService.ServiceApi.Name.Text(), service.ServiceApi.Name.Text())
}
} }
} }
return nil return nil
@ -276,55 +293,51 @@ func (p *Parser) checkTypeDeclaration(apiList []*Api) error {
for _, apiItem := range apiList { for _, apiItem := range apiList {
linePrefix := apiItem.LinePrefix linePrefix := apiItem.LinePrefix
for _, each := range apiItem.Type { err := p.checkTypes(apiItem, linePrefix, types)
tp, ok := each.(*TypeStruct) if err != nil {
if !ok { return err
continue }
}
for _, member := range tp.Fields { err = p.checkServices(apiItem, types, linePrefix)
err := p.checkType(linePrefix, types, member.DataType) if err != nil {
if err != nil { return err
return err
}
}
} }
}
return nil
}
for _, service := range apiItem.Service { func (p *Parser) checkServices(apiItem *Api, types map[string]TypeExpr, linePrefix string) error {
for _, each := range service.ServiceApi.ServiceRoute { for _, service := range apiItem.Service {
route := each.Route for _, each := range service.ServiceApi.ServiceRoute {
if route.Req != nil && route.Req.Name.IsNotNil() && route.Req.Name.Expr().IsNotNil() { route := each.Route
_, ok := types[route.Req.Name.Expr().Text()] err := p.checkRequestBody(route, types, linePrefix)
if !ok { if err != nil {
return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", return err
linePrefix, route.Req.Name.Expr().Line(), route.Req.Name.Expr().Column(), route.Req.Name.Expr().Text()) }
}
}
if route.Reply != nil && route.Reply.Name.IsNotNil() && route.Reply.Name.Expr().IsNotNil() { if route.Reply != nil && route.Reply.Name.IsNotNil() && route.Reply.Name.Expr().IsNotNil() {
reply := route.Reply.Name reply := route.Reply.Name
var structName string var structName string
switch tp := reply.(type) { switch tp := reply.(type) {
case *Literal:
structName = tp.Literal.Text()
case *Array:
switch innerTp := tp.Literal.(type) {
case *Literal: case *Literal:
structName = tp.Literal.Text() structName = innerTp.Literal.Text()
case *Array: case *Pointer:
switch innerTp := tp.Literal.(type) { structName = innerTp.Name.Text()
case *Literal:
structName = innerTp.Literal.Text()
case *Pointer:
structName = innerTp.Name.Text()
}
} }
}
if api.IsBasicType(structName) { if api.IsBasicType(structName) {
continue continue
} }
_, ok := types[structName] _, ok := types[structName]
if !ok { if !ok {
return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context", return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
linePrefix, route.Reply.Name.Expr().Line(), route.Reply.Name.Expr().Column(), structName) linePrefix, route.Reply.Name.Expr().Line(), route.Reply.Name.Expr().Column(), structName)
}
} }
} }
} }
@ -332,6 +345,34 @@ func (p *Parser) checkTypeDeclaration(apiList []*Api) error {
return nil return nil
} }
func (p *Parser) checkRequestBody(route *Route, types map[string]TypeExpr, linePrefix string) error {
if route.Req != nil && route.Req.Name.IsNotNil() && route.Req.Name.Expr().IsNotNil() {
_, ok := types[route.Req.Name.Expr().Text()]
if !ok {
return fmt.Errorf("%s line %d:%d can not found declaration '%s' in context",
linePrefix, route.Req.Name.Expr().Line(), route.Req.Name.Expr().Column(), route.Req.Name.Expr().Text())
}
}
return nil
}
func (p *Parser) checkTypes(apiItem *Api, linePrefix string, types map[string]TypeExpr) error {
for _, each := range apiItem.Type {
tp, ok := each.(*TypeStruct)
if !ok {
continue
}
for _, member := range tp.Fields {
err := p.checkType(linePrefix, types, member.DataType)
if err != nil {
return err
}
}
}
return nil
}
func (p *Parser) checkType(linePrefix string, types map[string]TypeExpr, expr DataType) error { func (p *Parser) checkType(linePrefix string, types map[string]TypeExpr, expr DataType) error {
if expr == nil { if expr == nil {
return nil return nil

@ -213,13 +213,7 @@ func (p parser) fillService() error {
var groups []spec.Group var groups []spec.Group
for _, item := range p.ast.Service { for _, item := range p.ast.Service {
var group spec.Group var group spec.Group
if item.AtServer != nil { p.fillAtServer(item, &group)
var properties = make(map[string]string, 0)
for _, kv := range item.AtServer.Kv {
properties[kv.Key.Text()] = kv.Value.Text()
}
group.Annotation.Properties = properties
}
for _, astRoute := range item.ServiceApi.ServiceRoute { for _, astRoute := range item.ServiceApi.ServiceRoute {
route := spec.Route{ route := spec.Route{
@ -231,25 +225,9 @@ func (p parser) fillService() error {
route.Handler = astRoute.AtHandler.Name.Text() route.Handler = astRoute.AtHandler.Name.Text()
} }
if astRoute.AtServer != nil { err := p.fillRouteAtServer(astRoute, &route)
var properties = make(map[string]string, 0) if err != nil {
for _, kv := range astRoute.AtServer.Kv { return err
properties[kv.Key.Text()] = kv.Value.Text()
}
route.Annotation.Properties = properties
if len(route.Handler) == 0 {
route.Handler = properties["handler"]
}
if len(route.Handler) == 0 {
return fmt.Errorf("missing handler annotation for %q", route.Path)
}
for _, char := range route.Handler {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return fmt.Errorf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
route.Path, route.Handler)
}
}
} }
if astRoute.Route.Req != nil { if astRoute.Route.Req != nil {
@ -269,7 +247,7 @@ func (p parser) fillService() error {
} }
} }
err := p.fillRouteType(&route) err = p.fillRouteType(&route)
if err != nil { if err != nil {
return err return err
} }
@ -289,6 +267,40 @@ func (p parser) fillService() error {
return nil return nil
} }
func (p parser) fillRouteAtServer(astRoute *ast.ServiceRoute, route *spec.Route) error {
if astRoute.AtServer != nil {
var properties = make(map[string]string, 0)
for _, kv := range astRoute.AtServer.Kv {
properties[kv.Key.Text()] = kv.Value.Text()
}
route.Annotation.Properties = properties
if len(route.Handler) == 0 {
route.Handler = properties["handler"]
}
if len(route.Handler) == 0 {
return fmt.Errorf("missing handler annotation for %q", route.Path)
}
for _, char := range route.Handler {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return fmt.Errorf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
route.Path, route.Handler)
}
}
}
return nil
}
func (p parser) fillAtServer(item *ast.Service, group *spec.Group) {
if item.AtServer != nil {
var properties = make(map[string]string, 0)
for _, kv := range item.AtServer.Kv {
properties[kv.Key.Text()] = kv.Value.Text()
}
group.Annotation.Properties = properties
}
}
func (p parser) fillRouteType(route *spec.Route) error { func (p parser) fillRouteType(route *spec.Route) error {
if route.RequestType != nil { if route.RequestType != nil {
switch route.RequestType.(type) { switch route.RequestType.(type) {

@ -1,58 +0,0 @@
package util
import (
"strconv"
"strings"
)
func TagLookup(tag, key string) (value string, ok bool) {
tag = strings.Replace(tag, "`", "", -1)
for tag != "" {
// Skip leading space.
i := 0
for i < len(tag) && tag[i] == ' ' {
i++
}
tag = tag[i:]
if tag == "" {
break
}
// Scan to colon. A space, a quote or a control character is a syntax error.
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
// as it is simpler to inspect the tag's bytes than the tag's runes.
i = 0
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
break
}
name := string(tag[:i])
tag = tag[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(tag) && tag[i] != '"' {
if tag[i] == '\\' {
i++
}
i++
}
if i >= len(tag) {
break
}
qvalue := string(tag[:i+1])
tag = tag[i+1:]
if key == name {
value, err := strconv.Unquote(qvalue)
if err != nil {
break
}
return value, true
}
}
return "", false
}

@ -28,7 +28,7 @@ import (
) )
var ( var (
BuildVersion = "1.1.5" buildVersion = "1.1.5"
commands = []cli.Command{ commands = []cli.Command{
{ {
Name: "upgrade", Name: "upgrade",
@ -510,7 +510,7 @@ func main() {
app := cli.NewApp() app := cli.NewApp()
app.Usage = "a cli tool to generate code" app.Usage = "a cli tool to generate code"
app.Version = fmt.Sprintf("%s %s/%s", BuildVersion, runtime.GOOS, runtime.GOARCH) app.Version = fmt.Sprintf("%s %s/%s", buildVersion, runtime.GOOS, runtime.GOARCH)
app.Commands = commands app.Commands = commands
// cli already print error messages // cli already print error messages
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {

@ -1,6 +1,7 @@
package gen package gen
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -31,7 +32,20 @@ type (
pkg string pkg string
cfg *config.Config cfg *config.Config
} }
Option func(generator *defaultGenerator) Option func(generator *defaultGenerator)
code struct {
importsCode string
varsCode string
typesCode string
newCode string
insertCode string
findCode []string
updateCode string
deleteCode string
cacheExtra string
}
) )
func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) { func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
@ -186,15 +200,6 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", fmt.Errorf("table %s: missing primary key", in.Name.Source()) return "", fmt.Errorf("table %s: missing primary key", in.Name.Source())
} }
text, err := util.LoadTemplate(category, modelTemplateFile, template.Model)
if err != nil {
return "", err
}
t := util.With("model").
Parse(text).
GoFmt(true)
m, err := genCacheKeys(in) m, err := genCacheKeys(in)
if err != nil { if err != nil {
return "", err return "", err
@ -261,18 +266,19 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", err return "", err
} }
output, err := t.Execute(map[string]interface{}{ code := &code{
"pkg": g.pkg, importsCode: importsCode,
"imports": importsCode, varsCode: varsCode,
"vars": varsCode, typesCode: typesCode,
"types": typesCode, newCode: newCode,
"new": newCode, insertCode: insertCode,
"insert": insertCode, findCode: findCode,
"find": strings.Join(findCode, "\n"), updateCode: updateCode,
"update": updateCode, deleteCode: deleteCode,
"delete": deleteCode, cacheExtra: ret.cacheExtra,
"extraMethod": ret.cacheExtra, }
})
output, err := g.executeModel(code)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -280,6 +286,32 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return output.String(), nil return output.String(), nil
} }
func (g *defaultGenerator) executeModel(code *code) (*bytes.Buffer, error) {
text, err := util.LoadTemplate(category, modelTemplateFile, template.Model)
if err != nil {
return nil, err
}
t := util.With("model").
Parse(text).
GoFmt(true)
output, err := t.Execute(map[string]interface{}{
"pkg": g.pkg,
"imports": code.importsCode,
"vars": code.varsCode,
"types": code.typesCode,
"new": code.newCode,
"insert": code.insertCode,
"find": strings.Join(code.findCode, "\n"),
"update": code.updateCode,
"delete": code.deleteCode,
"extraMethod": code.cacheExtra,
})
if err != nil {
return nil, err
}
return output, nil
}
func wrapWithRawString(v string) string { func wrapWithRawString(v string) string {
if v == "`" { if v == "`" {
return v return v

@ -68,40 +68,24 @@ func Parse(ddl string) (*Table, error) {
columns := tableSpec.Columns columns := tableSpec.Columns
indexes := tableSpec.Indexes indexes := tableSpec.Indexes
keyMap := make(map[string]KeyType) keyMap, err := getIndexKeyType(indexes)
for _, index := range indexes { if err != nil {
info := index.Info return nil, err
if info == nil { }
continue
}
if info.Primary {
if len(index.Columns) > 1 {
return nil, errPrimaryKey
}
keyMap[index.Columns[0].Column.String()] = primary fields, primaryKey, err := convertFileds(columns, keyMap)
continue if err != nil {
} return nil, err
// can optimize
if len(index.Columns) > 1 {
continue
}
column := index.Columns[0]
columnName := column.Column.String()
camelColumnName := stringx.From(columnName).ToCamel()
// by default, createTime|updateTime findOne is not used.
if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
continue
}
if info.Unique {
keyMap[columnName] = unique
} else if info.Spatial {
keyMap[columnName] = spatial
} else {
keyMap[columnName] = normal
}
} }
return &Table{
Name: stringx.From(tableName),
PrimaryKey: primaryKey,
Fields: fields,
}, nil
}
func convertFileds(columns []*sqlparser.ColumnDefinition, keyMap map[string]KeyType) ([]Field, Primary, error) {
var fields []Field var fields []Field
var primaryKey Primary var primaryKey Primary
for _, column := range columns { for _, column := range columns {
@ -124,7 +108,7 @@ func Parse(ddl string) (*Table, error) {
} }
dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull) dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull)
if err != nil { if err != nil {
return nil, err return nil, primaryKey, err
} }
var field Field var field Field
@ -145,12 +129,44 @@ func Parse(ddl string) (*Table, error) {
} }
fields = append(fields, field) fields = append(fields, field)
} }
return fields, primaryKey, nil
}
return &Table{ func getIndexKeyType(indexes []*sqlparser.IndexDefinition) (map[string]KeyType, error) {
Name: stringx.From(tableName), keyMap := make(map[string]KeyType)
PrimaryKey: primaryKey, for _, index := range indexes {
Fields: fields, info := index.Info
}, nil if info == nil {
continue
}
if info.Primary {
if len(index.Columns) > 1 {
return nil, errPrimaryKey
}
keyMap[index.Columns[0].Column.String()] = primary
continue
}
// can optimize
if len(index.Columns) > 1 {
continue
}
column := index.Columns[0]
columnName := column.Column.String()
camelColumnName := stringx.From(columnName).ToCamel()
// by default, createTime|updateTime findOne is not used.
if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
continue
}
if info.Unique {
keyMap[columnName] = unique
} else if info.Spatial {
keyMap[columnName] = spatial
} else {
keyMap[columnName] = normal
}
}
return keyMap, nil
} }
func (t *Table) ContainsTime() bool { func (t *Table) ContainsTime() bool {

@ -8,6 +8,9 @@ import (
) )
type ( type (
// Console wraps from the fmt.Sprintf,
// by default, it implemented the colorConsole to provide the colorful output to the consle
// and the ideaConsole to output with prefix for the plugin of intellij
Console interface { Console interface {
Success(format string, a ...interface{}) Success(format string, a ...interface{})
Info(format string, a ...interface{}) Info(format string, a ...interface{})
@ -25,6 +28,7 @@ type (
} }
) )
// NewConsole returns a instance of Console
func NewConsole(idea bool) Console { func NewConsole(idea bool) Console {
if idea { if idea {
return NewIdeaConsole() return NewIdeaConsole()
@ -32,7 +36,8 @@ func NewConsole(idea bool) Console {
return NewColorConsole() return NewColorConsole()
} }
func NewColorConsole() *colorConsole { // NewColorConsole returns a instance of colorConsole
func NewColorConsole() Console {
return &colorConsole{} return &colorConsole{}
} }
@ -76,7 +81,8 @@ func (c *colorConsole) Must(err error) {
} }
} }
func NewIdeaConsole() *ideaConsole { // NewIdeaConsole returns a instace of ideaConsole
func NewIdeaConsole() Console {
return &ideaConsole{} return &ideaConsole{}
} }

@ -9,6 +9,8 @@ import (
var errModuleCheck = errors.New("the work directory must be found in the go mod or the $GOPATH") var errModuleCheck = errors.New("the work directory must be found in the go mod or the $GOPATH")
// ProjectContext is a structure for the project,
// which contains WorkDir, Name, Path and Dir
type ProjectContext struct { type ProjectContext struct {
WorkDir string WorkDir string
// Name is the root name of the project // Name is the root name of the project

@ -9,6 +9,8 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
) )
// Module contains the relative data of go module,
// which is the result of the command go list
type Module struct { type Module struct {
Path string Path string
Main bool Main bool

@ -10,9 +10,7 @@ import (
"github.com/logrusorgru/aurora" "github.com/logrusorgru/aurora"
) )
const ( const NL = "\n"
NL = "\n"
)
func CreateIfNotExist(file string) (*os.File, error) { func CreateIfNotExist(file string) (*os.File, error) {
_, err := os.Stat(file) _, err := os.Stat(file)

@ -18,6 +18,7 @@ const (
upper upper
) )
// ErrNamingFormat defines an error for unknown fomat
var ErrNamingFormat = errors.New("unsupported format") var ErrNamingFormat = errors.New("unsupported format")
type ( type (

@ -1,3 +1,5 @@
// Package name provides methods to verify naming style and format naming style
// See the method IsNamingValid, FormatFilename
package name package name
import ( import (
@ -6,11 +8,15 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
// NamingStyle the type of string
type NamingStyle = string type NamingStyle = string
const ( const (
// NamingLower defines the lower spell case
NamingLower NamingStyle = "lower" NamingLower NamingStyle = "lower"
// NamingCamel defines the camel spell case
NamingCamel NamingStyle = "camel" NamingCamel NamingStyle = "camel"
// NamingSnake defines the snake spell case
NamingSnake NamingStyle = "snake" NamingSnake NamingStyle = "snake"
) )
@ -29,6 +35,8 @@ func IsNamingValid(namingStyle string) (NamingStyle, bool) {
} }
} }
// FormatFilename converts the filename string to the target
// naming style by calling method of stringx
func FormatFilename(filename string, style NamingStyle) string { func FormatFilename(filename string, style NamingStyle) string {
switch style { switch style {
case NamingCamel: case NamingCamel:

@ -6,14 +6,17 @@ import (
"unicode" "unicode"
) )
// String provides for coverting the source text into other spell case,like lower,snake,camel
type String struct { type String struct {
source string source string
} }
// From converts the input text to String and returns it
func From(data string) String { func From(data string) String {
return String{source: data} return String{source: data}
} }
// IsEmptyOrSpace returns true if the length of the string value is 0 after call strings.TrimSpace, or else returns false
func (s String) IsEmptyOrSpace() bool { func (s String) IsEmptyOrSpace() bool {
if len(s.source) == 0 { if len(s.source) == 0 {
return true return true
@ -24,18 +27,22 @@ func (s String) IsEmptyOrSpace() bool {
return false return false
} }
// Lower calls the strings.ToLower
func (s String) Lower() string { func (s String) Lower() string {
return strings.ToLower(s.source) return strings.ToLower(s.source)
} }
// ReplaceAll calls the strings.ReplaceAll
func (s String) ReplaceAll(old, new string) string { func (s String) ReplaceAll(old, new string) string {
return strings.ReplaceAll(s.source, old, new) return strings.ReplaceAll(s.source, old, new)
} }
//Source returns the source string value
func (s String) Source() string { func (s String) Source() string {
return s.source return s.source
} }
// Title calls the strings.Title
func (s String) Title() string { func (s String) Title() string {
if s.IsEmptyOrSpace() { if s.IsEmptyOrSpace() {
return s.source return s.source
@ -43,7 +50,7 @@ func (s String) Title() string {
return strings.Title(s.source) return strings.Title(s.source)
} }
// snake->camel(upper start) // ToCamel converts the input text into camel case
func (s String) ToCamel() string { func (s String) ToCamel() string {
list := s.splitBy(func(r rune) bool { list := s.splitBy(func(r rune) bool {
return r == '_' return r == '_'
@ -55,7 +62,7 @@ func (s String) ToCamel() string {
return strings.Join(target, "") return strings.Join(target, "")
} }
// camel->snake // ToSnake converts the input text into snake case
func (s String) ToSnake() string { func (s String) ToSnake() string {
list := s.splitBy(unicode.IsUpper, false) list := s.splitBy(unicode.IsUpper, false)
var target []string var target []string
@ -65,7 +72,7 @@ func (s String) ToSnake() string {
return strings.Join(target, "_") return strings.Join(target, "_")
} }
// return original string if rune is not letter at index 0 // Untitle return the original string if rune is not letter at index 0
func (s String) Untitle() string { func (s String) Untitle() string {
if s.IsEmptyOrSpace() { if s.IsEmptyOrSpace() {
return s.source return s.source
@ -77,10 +84,6 @@ func (s String) Untitle() string {
return string(unicode.ToLower(r)) + s.source[1:] return string(unicode.ToLower(r)) + s.source[1:]
} }
func (s String) Upper() string {
return strings.ToUpper(s.source)
}
// it will not ignore spaces // it will not ignore spaces
func (s String) splitBy(fn func(r rune) bool, remove bool) []string { func (s String) splitBy(fn func(r rune) bool, remove bool) []string {
if s.IsEmptyOrSpace() { if s.IsEmptyOrSpace() {

@ -9,29 +9,35 @@ import (
const regularPerm = 0666 const regularPerm = 0666
type defaultTemplate struct { // DefaultTemplate is a tool to provides the text/template operations
type DefaultTemplate struct {
name string name string
text string text string
goFmt bool goFmt bool
savePath string savePath string
} }
func With(name string) *defaultTemplate { // With returns a instace of DefaultTemplate
return &defaultTemplate{ func With(name string) *DefaultTemplate {
return &DefaultTemplate{
name: name, name: name,
} }
} }
func (t *defaultTemplate) Parse(text string) *defaultTemplate {
// Parse accepts a source template and returns DefaultTemplate
func (t *DefaultTemplate) Parse(text string) *DefaultTemplate {
t.text = text t.text = text
return t return t
} }
func (t *defaultTemplate) GoFmt(format bool) *defaultTemplate { // GoFmt sets the value to goFmt and marks the generated codes will be formated or not
func (t *DefaultTemplate) GoFmt(format bool) *DefaultTemplate {
t.goFmt = format t.goFmt = format
return t return t
} }
func (t *defaultTemplate) SaveTo(data interface{}, path string, forceUpdate bool) error { // SaveTo writes the codes to the target path
func (t *DefaultTemplate) SaveTo(data interface{}, path string, forceUpdate bool) error {
if FileExists(path) && !forceUpdate { if FileExists(path) && !forceUpdate {
return nil return nil
} }
@ -44,7 +50,8 @@ func (t *defaultTemplate) SaveTo(data interface{}, path string, forceUpdate bool
return ioutil.WriteFile(path, output.Bytes(), regularPerm) return ioutil.WriteFile(path, output.Bytes(), regularPerm)
} }
func (t *defaultTemplate) Execute(data interface{}) (*bytes.Buffer, error) { // Execute returns the codes after the template executed
func (t *DefaultTemplate) Execute(data interface{}) (*bytes.Buffer, error) {
tem, err := template.New(t.name).Parse(t.text) tem, err := template.New(t.name).Parse(t.text)
if err != nil { if err != nil {
return nil, err return nil, err

@ -1,9 +1,14 @@
package vars package vars
const ( const (
ProjectName = "zero" // ProjectName the const value of zero
ProjectOpenSourceUrl = "github.com/tal-tech/go-zero" ProjectName = "zero"
OsWindows = "windows" // ProjectOpenSourceURL the githb url of go-zero
OsMac = "darwin" ProjectOpenSourceURL = "github.com/tal-tech/go-zero"
OsLinux = "linux" // OsWindows windows os
OsWindows = "windows"
// OsMac mac os
OsMac = "darwin"
// OsLinux linux os
OsLinux = "linux"
) )

Loading…
Cancel
Save