Compare commits

...

12 Commits

Author SHA1 Message Date
Kevin Wan
e5c560e8ba simplify code, format makefile (#233) 2020-11-28 22:27:58 +08:00
xuezonggui
bed494d904 optimization (#221) 2020-11-28 19:43:39 +08:00
Keson
2dfecda465 modify the service name from proto (#230) 2020-11-28 11:48:44 +08:00
voidint
3ebb1e0221 Improve Makefile robustness (#224) 2020-11-27 23:40:07 +08:00
kingxt
348184904c set default handler value (#228)
* set default value

* set default value
2020-11-26 11:57:02 +08:00
Keson
7a27fa50a1 update version (#226) 2020-11-25 12:04:22 +08:00
Kevin Wan
8d4951c990 check go.mod before build docker image (#225) 2020-11-24 23:19:31 +08:00
Keson
6e57f6c527 feature model interface (#222)
* make variable declaration more concise

* add model interface

* optimize interface methods

* fix: go test failed

* warp returns

* optimize
2020-11-24 22:36:23 +08:00
kingxt
b9ac51b6c3 feature: file namestyle (#223)
* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-24 15:11:18 +08:00
kevin
702e8d79ce fix doc errors 2020-11-24 10:39:38 +08:00
kevin
95a9dabf8b format import 2020-11-23 16:35:39 +08:00
Chris
bae66c49c2 1.use local variable i; 2.make sure limiter larger than timer period (#218)
Co-authored-by: chris <feilee1987@163.com>
2020-11-23 16:34:51 +08:00
67 changed files with 1338 additions and 547 deletions

View File

@@ -76,7 +76,6 @@ func (rw *RollingWindow) updateOffset() {
span := rw.span()
if span > 0 {
offset := rw.offset
// reset expired buckets
start := offset + 1
steps := start + span
var remainder int
@@ -84,15 +83,16 @@ func (rw *RollingWindow) updateOffset() {
remainder = steps - rw.size
steps = rw.size
}
// reset expired buckets
for i := start; i < steps; i++ {
rw.win.resetBucket(i)
offset = i
}
for i := 0; i < remainder; i++ {
rw.win.resetBucket(i)
offset = i
}
rw.offset = offset
rw.offset = (offset + span) % rw.size
rw.lastTime = timex.Now()
}
}

View File

@@ -41,6 +41,7 @@ func main() {
var allowed, denied int32
var wait sync.WaitGroup
for i := 0; i < *threads; i++ {
i := i
wait.Add(1)
go func() {
for {

View File

@@ -174,7 +174,7 @@ goctl api -o greet.api
you can check it by curl:
```shell
curl -i http://localhost:8888/greet/from/you
curl -i http://localhost:8888/from/you
```
the response looks like:

View File

@@ -120,7 +120,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求:
```shell
curl -i http://localhost:8888/greet/from/you
curl -i http://localhost:8888/from/you
```
返回如下:

View File

@@ -2,13 +2,13 @@ version := $(shell /bin/date "+%Y-%m-%d %H:%M")
build:
go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go
command -v upx &> /dev/null && upx goctl
$(if $(shell command -v upx), upx goctl)
mac:
GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go
command -v upx &> /dev/null && upx goctl-darwin
$(if $(shell command -v upx), upx goctl-darwin)
win:
GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go
command -v upx &> /dev/null && upx goctl.exe
$(if $(shell command -v upx), upx goctl.exe)
linux:
GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go
command -v upx &> /dev/null && upx goctl-linux
$(if $(shell command -v upx), upx goctl-linux)

View File

@@ -28,10 +28,10 @@ type response struct {
}
service {{.serviceName}} {
@handler // TODO: set handler name and delete this comment
@handler GetUser // TODO: set handler name and delete this comment
get /users/id/:userId(request) returns(response)
@handler // TODO: set handler name and delete this comment
@handler CreateUser // TODO: set handler name and delete this comment
post /users/create(request)
}
`

View File

@@ -12,13 +12,13 @@ import (
"time"
"github.com/logrusorgru/aurora"
"github.com/urfave/cli"
"github.com/tal-tech/go-zero/core/logx"
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
const tmpFile = "%s-%d"
@@ -28,6 +28,8 @@ var tmpDir = path.Join(os.TempDir(), "goctl")
func GoCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")
namingStyle := c.String("style")
if len(apiFile) == 0 {
return errors.New("missing -api")
}
@@ -35,10 +37,10 @@ func GoCommand(c *cli.Context) error {
return errors.New("missing -dir")
}
return DoGenProject(apiFile, dir)
return DoGenProject(apiFile, dir, namingStyle)
}
func DoGenProject(apiFile, dir string) error {
func DoGenProject(apiFile, dir, style string) error {
p, err := parser.NewParser(apiFile)
if err != nil {
return err
@@ -48,15 +50,21 @@ func DoGenProject(apiFile, dir string) error {
return err
}
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
logx.Must(util.MkdirIfNotExist(dir))
logx.Must(genEtc(dir, api))
logx.Must(genConfig(dir, api))
logx.Must(genMain(dir, api))
logx.Must(genServiceContext(dir, api))
logx.Must(genTypes(dir, api))
logx.Must(genHandlers(dir, api))
logx.Must(genRoutes(dir, api))
logx.Must(genLogic(dir, api))
logx.Must(genEtc(dir, cfg, api))
logx.Must(genConfig(dir, cfg, api))
logx.Must(genMain(dir, cfg, api))
logx.Must(genServiceContext(dir, cfg, api))
logx.Must(genTypes(dir, cfg, api))
logx.Must(genRoutes(dir, cfg, api))
logx.Must(genHandlers(dir, cfg, api))
logx.Must(genLogic(dir, cfg, api))
logx.Must(genMiddleware(dir, cfg, api))
if err := backupAndSweep(apiFile); err != nil {
return err

View File

@@ -534,6 +534,7 @@ func TestHasImportApi(t *testing.T) {
}
}
assert.True(t, hasInline)
validate(t, filename)
}
@@ -558,15 +559,30 @@ func TestNestTypeApi(t *testing.T) {
err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
_, err = parser.NewParser(filename)
assert.NotNil(t, err)
}
func TestCamelStyle(t *testing.T) {
filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
_, err = parser.NewParser(filename)
assert.Nil(t, err)
validateWithCamel(t, filename, "GoZero")
}
func validate(t *testing.T, api string) {
validateWithCamel(t, api, "gozero")
}
func validateWithCamel(t *testing.T, api, camel string) {
dir := "_go"
os.RemoveAll(dir)
err := DoGenProject(api, dir)
err := DoGenProject(api, dir, camel)
defer os.RemoveAll(dir)
assert.Nil(t, err)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

View File

@@ -8,12 +8,14 @@ import (
"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/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
const (
configFile = "config.go"
configFile = "config"
configTemplate = `package config
import {{.authImport}}
@@ -31,8 +33,13 @@ type Config struct {
`
)
func genConfig(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, configDir, configFile)
func genConfig(dir string, cfg *config.Config, api *spec.ApiSpec) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, configFile)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, configDir, filename+".go")
if err != nil {
return err
}

View File

@@ -8,7 +8,9 @@ import (
"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/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
)
const (
@@ -20,8 +22,13 @@ Port: {{.port}}
`
)
func genEtc(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", api.Service.Name))
func genEtc(dir string, cfg *config.Config, api *spec.ApiSpec) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, api.Service.Name)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", filename))
if err != nil {
return err
}

View File

@@ -2,14 +2,18 @@ package gogen
import (
"bytes"
"errors"
"fmt"
"path"
"strings"
"text/template"
"unicode"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
@@ -50,13 +54,8 @@ type Handler struct {
HasRequest bool
}
func genHandler(dir string, group spec.Group, route spec.Route) error {
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
if !ok {
return fmt.Errorf("missing handler annotation for %q", route.Path)
}
handler = getHandlerName(handler)
func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler := getHandlerName(route)
if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler)
}
@@ -65,27 +64,24 @@ func genHandler(dir string, group spec.Group, route spec.Route) error {
return err
}
return doGenToFile(dir, handler, group, route, Handler{
return doGenToFile(dir, handler, cfg, group, route, Handler{
ImportPackages: genHandlerImports(group, route, parentPkg),
HandlerName: handler,
RequestType: util.Title(route.RequestType.Name),
LogicType: strings.TrimSuffix(strings.Title(handler), "Handler") + "Logic",
LogicType: strings.Title(getLogicName(route)),
Call: strings.Title(strings.TrimSuffix(handler, "Handler")),
HasResp: len(route.ResponseType.Name) > 0,
HasRequest: len(route.RequestType.Name) > 0,
})
}
func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handleObj Handler) error {
if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler)
}
filename := strings.ToLower(handler)
if strings.HasSuffix(filename, "handler") {
filename = filename + ".go"
} else {
filename = filename + "handler.go"
func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group, route spec.Route, handleObj Handler) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
if err != nil {
return err
}
filename = filename + ".go"
fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename)
if err != nil {
return err
@@ -111,10 +107,10 @@ func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handle
return err
}
func genHandlers(dir string, api *spec.ApiSpec) error {
func genHandlers(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, group := range api.Service.Groups {
for _, route := range group.Routes {
if err := genHandler(dir, group, route); err != nil {
if err := genHandler(dir, cfg, group, route); err != nil {
return err
}
}
@@ -136,14 +132,23 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
return strings.Join(imports, "\n\t")
}
func getHandlerBaseName(handler string) string {
handlerName := util.Untitle(handler)
if strings.HasSuffix(handlerName, "handler") {
handlerName = strings.ReplaceAll(handlerName, "handler", "")
} else if strings.HasSuffix(handlerName, "Handler") {
handlerName = strings.ReplaceAll(handlerName, "Handler", "")
func getHandlerBaseName(route spec.Route) (string, error) {
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
if !ok {
return "", fmt.Errorf("missing handler annotation for %q", route.Path)
}
return handlerName
for _, char := range handler {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return "", errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
route.Path, handler))
}
}
handler = strings.TrimSpace(handler)
handler = strings.TrimSuffix(handler, "handler")
handler = strings.TrimSuffix(handler, "Handler")
return handler, nil
}
func getHandlerFolderPath(group spec.Group, route spec.Route) string {
@@ -159,6 +164,20 @@ func getHandlerFolderPath(group spec.Group, route spec.Route) string {
return path.Join(handlerDir, folder)
}
func getHandlerName(handler string) string {
return getHandlerBaseName(handler) + "Handler"
func getHandlerName(route spec.Route) string {
handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Handler"
}
func getLogicName(route spec.Route) string {
handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Logic"
}

View File

@@ -9,7 +9,9 @@ import (
"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/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
@@ -40,10 +42,10 @@ func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
}
`
func genLogic(dir string, api *spec.ApiSpec) error {
func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, g := range api.Service.Groups {
for _, r := range g.Routes {
err := genLogicByRoute(dir, g, r)
err := genLogicByRoute(dir, cfg, g, r)
if err != nil {
return err
}
@@ -52,16 +54,14 @@ func genLogic(dir string, api *spec.ApiSpec) error {
return nil
}
func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
handler, ok := util.GetAnnotationValue(route.Annotations, "server", "handler")
if !ok {
return fmt.Errorf("missing handler annotation for %q", route.Path)
func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
logic := getLogicName(route)
goFile, err := format.FileNamingFormat(cfg.NamingFormat, logic)
if err != nil {
return err
}
handler = strings.TrimSuffix(handler, "handler")
handler = strings.TrimSuffix(handler, "Handler")
filename := strings.ToLower(handler)
goFile := filename + "logic.go"
goFile = goFile + ".go"
fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile)
if err != nil {
return err
@@ -102,8 +102,8 @@ func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
buffer := new(bytes.Buffer)
err = t.Execute(fp, map[string]string{
"imports": imports,
"logic": strings.Title(handler) + "Logic",
"function": strings.Title(strings.TrimSuffix(handler, "Handler")),
"logic": strings.Title(logic),
"function": strings.Title(strings.TrimSuffix(logic, "Logic")),
"responseType": responseString,
"returnString": returnString,
"request": requestString,

View File

@@ -8,7 +8,9 @@ import (
"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/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
@@ -40,12 +42,17 @@ func main() {
}
`
func genMain(dir string, api *spec.ApiSpec) error {
func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
name := strings.ToLower(api.Service.Name)
if strings.HasSuffix(name, "-api") {
name = strings.ReplaceAll(name, "-api", "")
}
goFile := name + ".go"
filename, err := format.FileNamingFormat(cfg.NamingFormat, name)
if err != nil {
return err
}
goFile := filename + ".go"
fp, created, err := util.MaybeCreateFile(dir, "", goFile)
if err != nil {
return err

View File

@@ -5,7 +5,10 @@ import (
"strings"
"text/template"
"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/config"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
)
var middlewareImplementCode = `
@@ -30,9 +33,16 @@ func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
}
`
func genMiddleware(dir string, middlewares []string) error {
func genMiddleware(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var middlewares = getMiddleware(api)
for _, item := range middlewares {
filename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "middleware" + ".go"
middlewareFilename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "_middleware"
formatName, err := format.FileNamingFormat(cfg.NamingFormat, middlewareFilename)
if err != nil {
return err
}
filename := formatName + ".go"
fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename)
if err != nil {
return err

View File

@@ -12,12 +12,14 @@ import (
"github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
const (
routesFilename = "routes.go"
routesFilename = "routes"
routesTemplate = `// Code generated by goctl. DO NOT EDIT.
package handler
@@ -62,7 +64,7 @@ type (
}
)
func genRoutes(dir string, api *spec.ApiSpec) error {
func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var builder strings.Builder
groups, err := getRoutes(api)
if err != nil {
@@ -121,10 +123,16 @@ func genRoutes(dir string, api *spec.ApiSpec) error {
return err
}
filename := path.Join(dir, handlerDir, routesFilename)
routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
if err != nil {
return err
}
routeFilename = routeFilename + ".go"
filename := path.Join(dir, handlerDir, routeFilename)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename)
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routeFilename)
if err != nil {
return err
}
@@ -176,11 +184,8 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
for _, g := range api.Service.Groups {
var groupedRoutes group
for _, r := range g.Routes {
handler, ok := apiutil.GetAnnotationValue(r.Annotations, "server", "handler")
if !ok {
return nil, fmt.Errorf("missing handler annotation for route %q", r.Path)
}
handler = getHandlerBaseName(handler) + "Handler(serverCtx)"
handler := getHandlerName(r)
handler = handler + "(serverCtx)"
folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty)
if ok {
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]

View File

@@ -8,12 +8,14 @@ import (
"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/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars"
)
const (
contextFilename = "servicecontext.go"
contextFilename = "service_context"
contextTemplate = `package svc
import (
@@ -35,8 +37,13 @@ func NewServiceContext(c {{.config}}) *ServiceContext {
`
)
func genServiceContext(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, contextDir, contextFilename)
func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, contextFilename)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, contextDir, filename+".go")
if err != nil {
return err
}
@@ -64,10 +71,6 @@ func genServiceContext(dir string, api *spec.ApiSpec) error {
var middlewareStr string
var middlewareAssignment string
var middlewares = getMiddleware(api)
err = genMiddleware(dir, middlewares)
if err != nil {
return err
}
for _, item := range middlewares {
middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item)

View File

@@ -12,11 +12,13 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
)
const (
typesFile = "types.go"
typesFile = "types"
typesTemplate = `// Code generated by goctl. DO NOT EDIT.
package types{{if .containsTime}}
import (
@@ -43,19 +45,25 @@ func BuildTypes(types []spec.Type) (string, error) {
return builder.String(), nil
}
func genTypes(dir string, api *spec.ApiSpec) error {
func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
val, err := BuildTypes(api.Types)
if err != nil {
return err
}
filename := path.Join(dir, typesDir, typesFile)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
typeFilename, err := format.FileNamingFormat(cfg.NamingFormat, typesFile)
if err != nil {
return err
}
typeFilename = typeFilename + ".go"
filename := path.Join(dir, typesDir, typeFilename)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typeFilename)
if err != nil {
return err
}
if !created {
return nil
}

View File

@@ -7,6 +7,7 @@ import (
"text/template"
"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
@@ -60,6 +61,6 @@ func NewService(c *cli.Context) error {
return err
}
err = gogen.DoGenProject(apiFilePath, abs)
err = gogen.DoGenProject(apiFilePath, abs, conf.DefaultFormat)
return err
}

View File

@@ -0,0 +1,122 @@
package config
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
"gopkg.in/yaml.v2"
)
const (
configFile = "config.yaml"
configFolder = "config"
DefaultFormat = "gozero"
)
const defaultYaml = `# namingFormat is used to define the naming format of the generated file name.
# just like time formatting, you can specify the formatting style through the
# two format characters go, and zero. for example: snake format you can
# define as go_zero, camel case format you can it is defined as goZero,
# and even split characters can be specified, such as go#zero. in theory,
# any combination can be used, but the prerequisite must meet the naming conventions
# of each operating system file name. if you want to independently control the file
# naming style of the api, rpc, and model layers, you can set it through apiNamingFormat,
# rpcNamingFormat, modelNamingFormat, and independent control is not enabled by default.
# for more information, please see #{apiNamingFormat},#{rpcNamingFormat},#{modelNamingFormat}
# Note: namingFormat is based on snake or camel string
namingFormat: gozero
`
type Config struct {
// NamingFormat is used to define the naming format of the generated file name.
// just like time formatting, you can specify the formatting style through the
// two format characters go, and zero. for example: snake format you can
// define as go_zero, camel case format you can it is defined as goZero,
// and even split characters can be specified, such as go#zero. in theory,
// any combination can be used, but the prerequisite must meet the naming conventions
// of each operating system file name.
// Note: NamingFormat is based on snake or camel string
NamingFormat string `yaml:"namingFormat"`
}
func NewConfig(format string) (*Config, error) {
if len(format) == 0 {
format = DefaultFormat
}
cfg := &Config{NamingFormat: format}
err := validate(cfg)
return cfg, err
}
func InitOrGetConfig() (*Config, error) {
var (
defaultConfig Config
)
err := yaml.Unmarshal([]byte(defaultYaml), &defaultConfig)
if err != nil {
return nil, err
}
goctlHome, err := util.GetGoctlHome()
if err != nil {
return nil, err
}
configDir := filepath.Join(goctlHome, configFolder)
configFilename := filepath.Join(configDir, configFile)
if util.FileExists(configFilename) {
data, err := ioutil.ReadFile(configFilename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, &defaultConfig)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
err = util.MkdirIfNotExist(configDir)
if err != nil {
return nil, err
}
f, err := os.Create(configFilename)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(defaultYaml)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
func validate(cfg *Config) error {
if len(strings.TrimSpace(cfg.NamingFormat)) == 0 {
return errors.New("missing namingFormat")
}
return nil
}

View File

@@ -0,0 +1,50 @@
# 配置项管理
| 名称 | 是否可选 | 说明 |
|-------------------|----------|-----------------------------------------------|
| namingFormat | YES | 文件名称格式化符 |
# naming-format
`namingFormat`可以用于对生成代码的文件名称进行格式化和日期格式化符yyyy-MM-dd类似在代码生成时可以根据这些配置项的格式化符进行格式化。
## 格式化符(gozero)
格式化符有`go`,`zero`组成,如常见的三种格式化风格你可以这样编写:
* lower: `gozero`
* camel: `goZero`
* snake: `go_zero`
常见格式化符生成示例
源字符welcome_to_go_zero
| 格式化符 | 格式化结果 | 说明 |
|------------|-----------------------|---------------------------|
| gozero | welcometogozero | 小写 |
| goZero | welcomeToGoZero | 驼峰 |
| go_zero | welcome_to_go_zero | snake |
| Go#zero | Welcome#to#go#zero | #号分割Title类型 |
| GOZERO | WELCOMETOGOZERO | 大写 |
| \_go#zero_ | \_welcome#to#go#zero_ | 下划线做前后缀,并且#分割 |
错误格式化符示例
* go
* gOZero
* zero
* goZEro
* goZERo
* goZeRo
* tal
# 使用方法
目前可通过在生成api、rpc、model时通过`--style`参数指定format格式
```shell script
goctl api go test.api -dir . -style gozero
```
```shell script
goctl rpc proto -src test.proto -dir . -style go_zero
```
```shell script
goctl model mysql datasource -url="" -table="*" -dir ./snake -style GoZero
```
# 默认值
当不指定-style时默认值为`gozero`

View File

@@ -18,6 +18,7 @@ ENV GOPROXY https://goproxy.cn,direct
WORKDIR /build/zero
COPY . .
RUN sh -c "[ -f go.mod ]" || exit
COPY {{.goRelPath}}/etc /app/etc
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}

View File

@@ -25,7 +25,7 @@ import (
)
var (
BuildVersion = "20201108"
BuildVersion = "20201125"
commands = []cli.Command{
{
Name: "api",
@@ -98,6 +98,11 @@ var (
Name: "api",
Usage: "the api file",
},
cli.StringFlag{
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
},
Action: gogen.GoCommand,
},
@@ -202,8 +207,9 @@ var (
Usage: `generate rpc demo service`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower",
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{
Name: "idea",
@@ -240,8 +246,9 @@ var (
Usage: `the target path of the code`,
},
cli.StringFlag{
Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower",
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{
Name: "idea",
@@ -273,8 +280,9 @@ var (
Usage: "the target dir",
},
cli.StringFlag{
Name: "style",
Usage: "the file naming style, lower|camel|snake,default is lower",
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{
Name: "cache, c",
@@ -308,8 +316,9 @@ var (
Usage: "the target dir",
},
cli.StringFlag{
Name: "style",
Usage: "the file naming style, lower|camel|snake, default is lower",
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{
Name: "idea",

View File

@@ -7,7 +7,7 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
* 通过ddl生成
```shell script
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c=true
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c
```
执行上述命令后即可快速生成CURD代码。
@@ -29,156 +29,191 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
```go
package model
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserPrefix = "cache#User#user#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
cacheUserIdPrefix = "cache#User#id#"
)
type (
UserModel interface {
Insert(data User) (sql.Result, error)
FindOne(id int64) (*User, error)
FindOneByUser(user string) (*User, error)
FindOneByName(name string) (*User, error)
FindOneByMobile(mobile string) (*User, error)
Update(data User) error
Delete(id int64) error
}
defaultUserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
User string `db:"user"` // 用户
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
return &defaultUserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *defaultUserModel) Insert(data User) (sql.Result, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userMobileKey, userKey, userNameKey)
return ret, err
}
func (m *defaultUserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByUser(user string) (*User, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user)
var resp User
err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where user = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, user); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *defaultUserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userIdKey, userKey, userNameKey, userMobileKey)
return err
}
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserIdPrefix = "cache#User#id#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
)
type (
UserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) *UserModel {
return &UserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *UserModel) Insert(data User) (sql.Result, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userNameKey, userMobileKey)
return ret, err
}
func (m *UserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *UserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userMobileKey, userIdKey, userNameKey)
return err
}
func (m *UserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *UserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
```
## 用法
@@ -211,25 +246,24 @@ OPTIONS:
* ddl
```shell script
goctl model mysql -src={patterns} -dir={dir} -cache=true
goctl model mysql -src={patterns} -dir={dir} -cache
```
help
```
NAME:
goctl model mysql ddl - generate mysql model from ddl
USAGE:
goctl model mysql ddl [command options] [arguments...]
OPTIONS:
--src value, -s value the path or path globbing patterns of the ddl
--dir value, -d value the target dir
--style value the file naming style, lower|camel|underline,default is lower
--cache, -c generate code with cache [optional]
--idea for idea plugin [optional]
goctl model mysql ddl - generate mysql model from ddl
USAGE:
goctl model mysql ddl [command options] [arguments...]
OPTIONS:
--src value, -s value the path or path globbing patterns of the ddl
--dir value, -d value the target dir
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--cache, -c generate code with cache [optional]
--idea for idea plugin [optional]
```
* datasource
@@ -242,18 +276,19 @@ OPTIONS:
```
NAME:
goctl model mysql datasource - generate model from datasource
goctl model mysql datasource - generate model from datasource
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--idea for idea plugin [optional]
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming style, lower|camel|snake, default is lower
--idea for idea plugin [optional]
```
@@ -281,13 +316,13 @@ OPTIONS:
* ddl
```shell script
goctl model -src={patterns} -dir={dir} -cache=false
goctl model -src={patterns} -dir={dir}
```
* datasource
```shell script
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} -cache=false
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}
```
生成代码仅基本的CURD结构。

View File

@@ -2,7 +2,6 @@ package command
import (
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
@@ -10,6 +9,7 @@ import (
"github.com/go-sql-driver/mysql"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
@@ -24,9 +24,9 @@ const (
flagDir = "dir"
flagCache = "cache"
flagIdea = "idea"
flagStyle = "style"
flagUrl = "url"
flagTable = "table"
flagStyle = "style"
)
func MysqlDDL(ctx *cli.Context) error {
@@ -34,8 +34,12 @@ func MysqlDDL(ctx *cli.Context) error {
dir := ctx.String(flagDir)
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle))
return fromDDl(src, dir, namingStyle, cache, idea)
style := ctx.String(flagStyle)
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDDl(src, dir, cfg, cache, idea)
}
func MyDataSource(ctx *cli.Context) error {
@@ -43,26 +47,23 @@ func MyDataSource(ctx *cli.Context) error {
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle))
style := ctx.String(flagStyle)
pattern := strings.TrimSpace(ctx.String(flagTable))
return fromDataSource(url, pattern, dir, namingStyle, cache, idea)
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDataSource(url, pattern, dir, cfg, cache, idea)
}
func fromDDl(src, dir, namingStyle string, cache, idea bool) error {
func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea)
src = strings.TrimSpace(src)
if len(src) == 0 {
return errors.New("expected path or path globbing patterns, but nothing found")
}
switch namingStyle {
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
files, err := util.MatchFiles(src)
if err != nil {
return err
@@ -81,7 +82,7 @@ func fromDDl(src, dir, namingStyle string, cache, idea bool) error {
source = append(source, string(data))
}
generator, err := gen.NewDefaultGenerator(dir, namingStyle, gen.WithConsoleOption(log))
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil {
return err
}
@@ -90,7 +91,7 @@ func fromDDl(src, dir, namingStyle string, cache, idea bool) error {
return err
}
func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) error {
func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea)
if len(url) == 0 {
log.Error("%v", "expected data source of mysql, but nothing found")
@@ -102,25 +103,17 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
return nil
}
switch namingStyle {
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
cfg, err := mysql.ParseDSN(url)
dsn, err := mysql.ParseDSN(url)
if err != nil {
return err
}
logx.Disable()
databaseSource := strings.TrimSuffix(url, "/"+cfg.DBName) + "/information_schema"
databaseSource := strings.TrimSuffix(url, "/"+dsn.DBName) + "/information_schema"
db := sqlx.NewMysql(databaseSource)
im := model.NewInformationSchemaModel(db)
tables, err := im.GetAllTables(cfg.DBName)
tables, err := im.GetAllTables(dsn.DBName)
if err != nil {
return err
}
@@ -135,7 +128,7 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
if !match {
continue
}
columns, err := im.FindByTableName(cfg.DBName, item)
columns, err := im.FindByTableName(dsn.DBName, item)
if err != nil {
return err
}
@@ -146,11 +139,11 @@ func fromDataSource(url, pattern, dir, namingStyle string, cache, idea bool) err
return errors.New("no tables matched")
}
generator, err := gen.NewDefaultGenerator(dir, namingStyle, gen.WithConsoleOption(log))
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil {
return err
}
err = generator.StartFromInformationSchema(cfg.DBName, matchTables, cache)
err = generator.StartFromInformationSchema(dsn.DBName, matchTables, cache)
return err
}

View File

@@ -7,19 +7,22 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
var sql = "-- 用户表 --\nCREATE TABLE `user` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n UNIQUE KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n"
var cfg = &config.Config{
NamingFormat: "gozero",
}
func TestFromDDl(t *testing.T) {
err := fromDDl("./user.sql", t.TempDir(), gen.NamingCamel, true, false)
err := fromDDl("./user.sql", t.TempDir(), cfg, true, false)
assert.Equal(t, errNotMatched, err)
// case dir is not exists
unknownDir := filepath.Join(t.TempDir(), "test", "user.sql")
err = fromDDl(unknownDir, t.TempDir(), gen.NamingCamel, true, false)
err = fromDDl(unknownDir, t.TempDir(), cfg, true, false)
assert.True(t, func() bool {
switch err.(type) {
case *os.PathError:
@@ -30,18 +33,11 @@ func TestFromDDl(t *testing.T) {
}())
// case empty src
err = fromDDl("", t.TempDir(), gen.NamingCamel, true, false)
err = fromDDl("", t.TempDir(), cfg, true, false)
if err != nil {
assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
}
// case unknown naming style
tmp := filepath.Join(t.TempDir(), "user.sql")
err = fromDDl(tmp, t.TempDir(), "lower1", true, false)
if err != nil {
assert.Equal(t, "unexpected naming style: lower1", err.Error())
}
tempDir := filepath.Join(t.TempDir(), "test")
err = util.MkdirIfNotExist(tempDir)
if err != nil {
@@ -67,7 +63,7 @@ func TestFromDDl(t *testing.T) {
_, err = os.Stat(user2Sql)
assert.Nil(t, err)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, gen.NamingLower, true, false)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false)
assert.Nil(t, err)
_, err = os.Stat(filepath.Join(tempDir, "usermodel.go"))

View File

@@ -2,7 +2,7 @@
# generate model with cache from ddl
fromDDL:
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -c
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -cache
# generate model with cache from data source
@@ -12,4 +12,4 @@ datasource=127.0.0.1:3306
database=gozero
fromDataSource:
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style camel
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero

View File

@@ -1,6 +1,7 @@
-- 用户表 --
CREATE TABLE `user` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
@@ -10,6 +11,7 @@ CREATE TABLE `user` (
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `user_index` (`user`),
UNIQUE KEY `mobile_index` (`mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genDelete(table Table, withCache bool) (string, error) {
func genDelete(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet()
keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey {
@@ -24,7 +24,7 @@ func genDelete(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete)
if err != nil {
return "", err
return "", "", err
}
output, err := util.With("delete").
@@ -40,8 +40,23 @@ func genDelete(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
})
if err != nil {
return "", err
return "", "", err
}
return output.String(), nil
// interface method
text, err = util.LoadTemplate(category, deleteMethodTemplateFile, template.DeleteMethod)
if err != nil {
return "", "", err
}
deleteMethodOut, err := util.With("deleteMethod").
Parse(text).
Execute(map[string]interface{}{
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), deleteMethodOut.String(), nil
}

View File

@@ -6,11 +6,11 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genFindOne(table Table, withCache bool) (string, error) {
func genFindOne(table Table, withCache bool) (string, string, error) {
camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
if err != nil {
return "", err
return "", "", err
}
output, err := util.With("findOne").
@@ -26,8 +26,23 @@ func genFindOne(table Table, withCache bool) (string, error) {
"cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable,
})
if err != nil {
return "", err
return "", "", err
}
return output.String(), nil
text, err = util.LoadTemplate(category, findOneMethodTemplateFile, template.FindOneMethod)
if err != nil {
return "", "", err
}
findOneMethod, err := util.With("findOneMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), findOneMethod.String(), nil
}

View File

@@ -9,10 +9,16 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genFindOneByField(table Table, withCache bool) (string, string, error) {
type findOneCode struct {
findOneMethod string
findOneInterfaceMethod string
cacheExtra string
}
func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
if err != nil {
return "", "", err
return nil, err
}
t := util.With("findOneByField").Parse(text)
@@ -36,15 +42,40 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalField": field.Name.Source(),
})
if err != nil {
return "", "", err
return nil, err
}
list = append(list, output.String())
}
text, err = util.LoadTemplate(category, findOneByFieldMethodTemplateFile, template.FindOneByFieldMethod)
if err != nil {
return nil, err
}
t = util.With("findOneByFieldMethod").Parse(text)
var listMethod []string
for _, field := range table.Fields {
if field.IsPrimaryKey || !field.IsUniqueKey {
continue
}
camelFieldName := field.Name.ToCamel()
output, err := t.Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
"upperField": camelFieldName,
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType),
})
if err != nil {
return nil, err
}
listMethod = append(listMethod, output.String())
}
if withCache {
text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod)
if err != nil {
return "", "", err
return nil, err
}
out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{
@@ -54,11 +85,18 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalPrimaryField": table.PrimaryKey.Name.Source(),
})
if err != nil {
return "", "", err
return nil, err
}
return strings.Join(list, "\n"), out.String(), nil
return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
cacheExtra: out.String(),
}, nil
}
return strings.Join(list, "\n"), "", nil
return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
}, nil
}

View File

@@ -7,11 +7,14 @@ import (
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
modelutil "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -28,13 +31,13 @@ type (
//source string
dir string
console.Console
pkg string
namingStyle string
pkg string
cfg *config.Config
}
Option func(generator *defaultGenerator)
)
func NewDefaultGenerator(dir, namingStyle string, opt ...Option) (*defaultGenerator, error) {
func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
if dir == "" {
dir = pwd
}
@@ -50,7 +53,7 @@ func NewDefaultGenerator(dir, namingStyle string, opt ...Option) (*defaultGenera
return nil, err
}
generator := &defaultGenerator{dir: dir, namingStyle: namingStyle, pkg: pkg}
generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg}
var optionList []Option
optionList = append(optionList, newDefaultOption())
optionList = append(optionList, opt...)
@@ -114,13 +117,12 @@ func (g *defaultGenerator) createFile(modelList map[string]string) error {
for tableName, code := range modelList {
tn := stringx.From(tableName)
name := fmt.Sprintf("%smodel.go", strings.ToLower(tn.ToCamel()))
switch g.namingStyle {
case NamingCamel:
name = fmt.Sprintf("%sModel.go", tn.ToCamel())
case NamingSnake:
name = fmt.Sprintf("%s_model.go", tn.ToSnake())
modelFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, fmt.Sprintf("%s_model", tn.Source()))
if err != nil {
return err
}
name := modelFilename + ".go"
filename := filepath.Join(dirAbs, name)
if util.FileExists(filename) {
g.Warning("%s already exists, ignored.", name)
@@ -132,10 +134,12 @@ func (g *defaultGenerator) createFile(modelList map[string]string) error {
}
}
// generate error file
filename := filepath.Join(dirAbs, "vars.go")
if g.namingStyle == NamingCamel {
filename = filepath.Join(dirAbs, "Vars.go")
varFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, "vars")
if err != nil {
return err
}
filename := filepath.Join(dirAbs, varFilename+".go")
text, err := util.LoadTemplate(category, errTemplateFile, template.Error)
if err != nil {
return err
@@ -219,39 +223,41 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", err
}
typesCode, err := genTypes(table, withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil {
return "", err
}
insertCode, err := genInsert(table, withCache)
insertCode, insertCodeMethod, err := genInsert(table, withCache)
if err != nil {
return "", err
}
var findCode = make([]string, 0)
findOneCode, err := genFindOne(table, withCache)
findOneCode, findOneCodeMethod, err := genFindOne(table, withCache)
if err != nil {
return "", err
}
findOneByFieldCode, extraMethod, err := genFindOneByField(table, withCache)
ret, err := genFindOneByField(table, withCache)
if err != nil {
return "", err
}
findCode = append(findCode, findOneCode, findOneByFieldCode)
updateCode, err := genUpdate(table, withCache)
findCode = append(findCode, findOneCode, ret.findOneMethod)
updateCode, updateCodeMethod, err := genUpdate(table, withCache)
if err != nil {
return "", err
}
deleteCode, err := genDelete(table, withCache)
deleteCode, deleteCodeMethod, err := genDelete(table, withCache)
if err != nil {
return "", err
}
var list []string
list = append(list, insertCodeMethod, findOneCodeMethod, ret.findOneInterfaceMethod, updateCodeMethod, deleteCodeMethod)
typesCode, err := genTypes(table, strings.Join(modelutil.TrimStringSlice(list), util.NL), withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil {
return "", err
}
@@ -266,7 +272,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
"find": strings.Join(findCode, "\n"),
"update": updateCode,
"delete": deleteCode,
"extraMethod": extraMethod,
"extraMethod": ret.cacheExtra,
})
if err != nil {
return "", err

View File

@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/tools/goctl/config"
)
var (
@@ -22,7 +23,9 @@ func TestCacheModel(t *testing.T) {
defer func() {
_ = os.RemoveAll(dir)
}()
g, err := NewDefaultGenerator(cacheDir, NamingCamel)
g, err := NewDefaultGenerator(cacheDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
@@ -31,7 +34,9 @@ func TestCacheModel(t *testing.T) {
_, err := os.Stat(filepath.Join(cacheDir, "TestUserInfoModel.go"))
return err == nil
}())
g, err = NewDefaultGenerator(noCacheDir, NamingLower)
g, err = NewDefaultGenerator(noCacheDir, &config.Config{
NamingFormat: "gozero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, false)
@@ -51,7 +56,9 @@ func TestNamingModel(t *testing.T) {
defer func() {
_ = os.RemoveAll(dir)
}()
g, err := NewDefaultGenerator(camelDir, NamingCamel)
g, err := NewDefaultGenerator(camelDir, &config.Config{
NamingFormat: "GoZero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
@@ -60,7 +67,9 @@ func TestNamingModel(t *testing.T) {
_, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go"))
return err == nil
}())
g, err = NewDefaultGenerator(snakeDir, NamingSnake)
g, err = NewDefaultGenerator(snakeDir, &config.Config{
NamingFormat: "go_zero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genInsert(table Table, withCache bool) (string, error) {
func genInsert(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet()
keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey {
@@ -36,7 +36,7 @@ func genInsert(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert)
if err != nil {
return "", err
return "", "", err
}
output, err := util.With("insert").
@@ -52,8 +52,23 @@ func genInsert(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
})
if err != nil {
return "", err
return "", "", err
}
return output.String(), nil
// interface method
text, err = util.LoadTemplate(category, insertTemplateMethodFile, template.InsertMethod)
if err != nil {
return "", "", err
}
insertMethodOutput, err := util.With("insertMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
})
if err != nil {
return "", "", err
}
return output.String(), insertMethodOutput.String(), nil
}

View File

@@ -2,6 +2,7 @@ package gen
import (
"fmt"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
@@ -33,8 +34,14 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) {
camelFieldName := field.Name.ToCamel()
lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle()
left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName)
if strings.ToLower(camelFieldName) == strings.ToLower(camelTableName) {
left = fmt.Sprintf("cache%sPrefix", camelTableName)
}
right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName)
variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)
if strings.ToLower(lowerStartCamelTableName) == strings.ToLower(camelFieldName) {
variable = fmt.Sprintf("%sKey", lowerStartCamelTableName)
}
m[field.Name.Source()] = Key{
VarExpression: fmt.Sprintf(`%s = "%s"`, left, right),
Left: left,

View File

@@ -11,31 +11,40 @@ import (
const (
category = "model"
deleteTemplateFile = "delete.tpl"
deleteMethodTemplateFile = "interface-delete.tpl"
fieldTemplateFile = "filed.tpl"
findOneTemplateFile = "find-one.tpl"
findOneMethodTemplateFile = "interface-find-one.tpl"
findOneByFieldTemplateFile = "find-one-by-field.tpl"
findOneByFieldMethodTemplateFile = "interface-find-one-by-field.tpl"
findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl"
importsTemplateFile = "import.tpl"
importsWithNoCacheTemplateFile = "import-no-cache.tpl"
insertTemplateFile = "insert.tpl"
insertTemplateMethodFile = "interface-insert.tpl"
modelTemplateFile = "model.tpl"
modelNewTemplateFile = "model-new.tpl"
tagTemplateFile = "tag.tpl"
typesTemplateFile = "types.tpl"
updateTemplateFile = "update.tpl"
updateMethodTemplateFile = "interface-update.tpl"
varTemplateFile = "var.tpl"
errTemplateFile = "err.tpl"
)
var templates = map[string]string{
deleteTemplateFile: template.Delete,
deleteMethodTemplateFile: template.DeleteMethod,
fieldTemplateFile: template.Field,
findOneTemplateFile: template.FindOne,
findOneMethodTemplateFile: template.FindOneMethod,
findOneByFieldTemplateFile: template.FindOneByField,
findOneByFieldMethodTemplateFile: template.FindOneByFieldMethod,
findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod,
importsTemplateFile: template.Imports,
importsWithNoCacheTemplateFile: template.ImportsNoCache,
insertTemplateFile: template.Insert,
insertTemplateMethodFile: template.InsertMethod,
modelTemplateFile: template.Model,
modelNewTemplateFile: template.New,
tagTemplateFile: template.Tag,

View File

@@ -5,7 +5,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util"
)
func genTypes(table Table, withCache bool) (string, error) {
func genTypes(table Table, methods string, withCache bool) (string, error) {
fields := table.Fields
fieldsString, err := genFields(fields)
if err != nil {
@@ -21,6 +21,7 @@ func genTypes(table Table, withCache bool) (string, error) {
Parse(text).
Execute(map[string]interface{}{
"withCache": withCache,
"method": methods,
"upperStartCamelObject": table.Name.ToCamel(),
"fields": fieldsString,
})

View File

@@ -8,7 +8,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genUpdate(table Table, withCache bool) (string, error) {
func genUpdate(table Table, withCache bool) (string, string, error) {
expressionValues := make([]string, 0)
for _, filed := range table.Fields {
camel := filed.Name.ToCamel()
@@ -24,7 +24,7 @@ func genUpdate(table Table, withCache bool) (string, error) {
camelTableName := table.Name.ToCamel()
text, err := util.LoadTemplate(category, updateTemplateFile, template.Update)
if err != nil {
return "", err
return "", "", err
}
output, err := util.With("update").
@@ -39,8 +39,23 @@ func genUpdate(table Table, withCache bool) (string, error) {
"expressionValues": strings.Join(expressionValues, ", "),
})
if err != nil {
return "", nil
return "", "", nil
}
return output.String(), nil
// update interface method
text, err = util.LoadTemplate(category, updateMethodTemplateFile, template.UpdateMethod)
if err != nil {
return "", "", err
}
updateMethodOutput, err := util.With("updateMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
})
if err != nil {
return "", "", nil
}
return output.String(), updateMethodOutput.String(), nil
}

View File

@@ -76,6 +76,9 @@ func TestConvertColumn(t *testing.T) {
assert.Nil(t, err)
assert.True(t, table.PrimaryKey.AutoIncrement && table.PrimaryKey.IsPrimaryKey)
assert.Equal(t, "id", table.PrimaryKey.Name.Source())
assert.Equal(t, "mobile", table.Fields[1].Name.Source())
assert.True(t, table.Fields[1].IsUniqueKey)
for _, item := range table.Fields {
if item.Name.Source() == "mobile" {
assert.True(t, item.IsUniqueKey)
}
}
}

View File

@@ -1,7 +1,7 @@
package template
var Delete = `
func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}})
if err!=nil{
return err
@@ -16,3 +16,5 @@ func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}}
return err
}
`
var DeleteMethod = `Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error`

View File

@@ -2,7 +2,7 @@ package template
// 通过id查询
var FindOne = `
func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {
func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}}
err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error {
@@ -32,7 +32,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}}
// 通过指定字段查询
var FindOneByField = `
func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) {
func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}}
err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
@@ -64,12 +64,15 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{
}{{end}}
`
var FindOneByFieldExtraMethod = `
func (m *{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string {
func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary)
}
func (m *{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
func (m *default{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
return conn.QueryRow(v, query, primary)
}
`
var FindOneMethod = `FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)`
var FindOneByFieldMethod = `FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) `

View File

@@ -1,7 +1,7 @@
package template
var Insert = `
func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
{{if .withCache}}{{if .containsIndexCache}}{{.keys}}
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)
@@ -13,3 +13,5 @@ func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}
return ret,err
}
`
var InsertMethod = `Insert(data {{.upperStartCamelObject}}) (sql.Result,error)`

View File

@@ -1,8 +1,8 @@
package template
var New = `
func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *{{.upperStartCamelObject}}Model {
return &{{.upperStartCamelObject}}Model{
func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model {
return &default{{.upperStartCamelObject}}Model{
{{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}},
table: "{{.table}}",
}

View File

@@ -2,7 +2,11 @@ package template
var Types = `
type (
{{.upperStartCamelObject}}Model struct {
{{.upperStartCamelObject}}Model interface{
{{.method}}
}
default{{.upperStartCamelObject}}Model struct {
{{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}}
table string
}

View File

@@ -1,7 +1,7 @@
package template
var Update = `
func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
{{if .withCache}}{{.primaryCacheKey}}
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)
@@ -11,3 +11,5 @@ func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}
return err
}
`
var UpdateMethod = `Update(data {{.upperStartCamelObject}}) error`

View File

@@ -0,0 +1,12 @@
package util
func TrimStringSlice(list []string) []string {
var out []string
for _, item := range list {
if len(item) == 0 {
continue
}
out = append(out, item)
}
return out
}

View File

@@ -24,32 +24,26 @@ func Rpc(c *cli.Context) error {
return errors.New("missing -dir")
}
namingStyle, valid := generator.IsNamingValid(style)
if !valid {
return fmt.Errorf("unexpected naming style %s", style)
g, err := generator.NewDefaultRpcGenerator(style)
if err != nil {
return err
}
g := generator.NewDefaultRpcGenerator(namingStyle)
return g.Generate(src, out, protoImportPath)
}
// RpcNew is to generate rpc greet service, this greet service can speed
// up your understanding of the zrpc service structure
func RpcNew(c *cli.Context) error {
name := c.Args().First()
ext := filepath.Ext(name)
rpcname := c.Args().First()
ext := filepath.Ext(rpcname)
if len(ext) > 0 {
return fmt.Errorf("unexpected ext: %s", ext)
}
style := c.String("style")
namingStyle, valid := generator.IsNamingValid(style)
if !valid {
return fmt.Errorf("expected naming style [lower|camel|snake], but found %s", style)
}
protoName := name + ".proto"
filename := filepath.Join(".", name, protoName)
protoName := rpcname + ".proto"
filename := filepath.Join(".", rpcname, protoName)
src, err := filepath.Abs(filename)
if err != nil {
return err
@@ -60,7 +54,11 @@ func RpcNew(c *cli.Context) error {
return err
}
g := generator.NewDefaultRpcGenerator(namingStyle)
g, err := generator.NewDefaultRpcGenerator(style)
if err != nil {
return err
}
return g.Generate(src, filepath.Dir(src), nil)
}

View File

@@ -1,18 +0,0 @@
package generator
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func formatFilename(filename string, style NamingStyle) string {
switch style {
case namingCamel:
return stringx.From(filename).ToCamel()
case namingSnake:
return stringx.From(filename).ToSnake()
default:
return strings.ToLower(stringx.From(filename).ToCamel())
}
}

View File

@@ -1,17 +0,0 @@
package generator
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatFilename(t *testing.T) {
assert.Equal(t, "abc", formatFilename("a_b_c", namingLower))
assert.Equal(t, "ABC", formatFilename("a_b_c", namingCamel))
assert.Equal(t, "a_b_c", formatFilename("a_b_c", namingSnake))
assert.Equal(t, "a", formatFilename("a", namingSnake))
assert.Equal(t, "A", formatFilename("a", namingCamel))
// no flag to convert to snake
assert.Equal(t, "abc", formatFilename("abc", namingSnake))
}

View File

@@ -3,6 +3,7 @@ package generator
import (
"path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
@@ -10,18 +11,22 @@ import (
)
type RpcGenerator struct {
g Generator
style NamingStyle
g Generator
cfg *conf.Config
}
func NewDefaultRpcGenerator(style NamingStyle) *RpcGenerator {
return NewRpcGenerator(NewDefaultGenerator(), style)
func NewDefaultRpcGenerator(style string) (*RpcGenerator, error) {
cfg, err := conf.NewConfig(style)
if err != nil {
return nil, err
}
return NewRpcGenerator(NewDefaultGenerator(), cfg), nil
}
func NewRpcGenerator(g Generator, style NamingStyle) *RpcGenerator {
func NewRpcGenerator(g Generator, cfg *conf.Config) *RpcGenerator {
return &RpcGenerator{
g: g,
style: style,
g: g,
cfg: cfg,
}
}
@@ -57,42 +62,42 @@ func (g *RpcGenerator) Generate(src, target string, protoImportPath []string) er
return err
}
err = g.g.GenEtc(dirCtx, proto, g.style)
err = g.g.GenEtc(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenPb(dirCtx, protoImportPath, proto, g.style)
err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenConfig(dirCtx, proto, g.style)
err = g.g.GenConfig(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenSvc(dirCtx, proto, g.style)
err = g.g.GenSvc(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenLogic(dirCtx, proto, g.style)
err = g.g.GenLogic(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenServer(dirCtx, proto, g.style)
err = g.g.GenServer(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenMain(dirCtx, proto, g.style)
err = g.g.GenMain(dirCtx, proto, g.cfg)
if err != nil {
return err
}
err = g.g.GenCall(dirCtx, proto, g.style)
err = g.g.GenCall(dirCtx, proto, g.cfg)
console.NewColorConsole().MarkDone()

View File

@@ -9,9 +9,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stringx"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
)
var cfg = &conf.Config{
NamingFormat: "gozero",
}
func TestRpcGenerate(t *testing.T) {
_ = Clean()
dispatcher := NewDefaultGenerator()
@@ -21,7 +26,7 @@ func TestRpcGenerate(t *testing.T) {
return
}
projectName := stringx.Rand()
g := NewRpcGenerator(dispatcher, namingLower)
g := NewRpcGenerator(dispatcher, cfg)
// case go path
src := filepath.Join(build.Default.GOPATH, "src")

View File

@@ -6,8 +6,10 @@ import (
"strings"
"github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -59,12 +61,17 @@ func (m *default{{.serviceName}}) {{.method}}(ctx context.Context,in *{{.pbReque
`
)
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetCall()
service := proto.Service
head := util.GetHead(proto.Name)
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", formatFilename(service.Name, namingStyle)))
callFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name)
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", callFilename))
functions, err := g.genFunction(proto.PbPackage, service)
if err != nil {
return err
@@ -86,7 +93,7 @@ func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, namingSty
}
err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": formatFilename(service.Name, namingStyle),
"name": callFilename,
"alias": strings.Join(alias.KeysStr(), util.NL),
"head": head,
"filePackage": dir.Base,

View File

@@ -5,8 +5,10 @@ import (
"os"
"path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
)
const configTemplate = `package config
@@ -18,9 +20,14 @@ type Config struct {
}
`
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetConfig()
fileName := filepath.Join(dir.Filename, formatFilename("config", namingStyle)+".go")
configFilename, err := format.FileNamingFormat(cfg.NamingFormat, "config")
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, configFilename+".go")
if util.FileExists(fileName) {
return nil
}

View File

@@ -1,15 +1,18 @@
package generator
import "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
import (
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
type Generator interface {
Prepare() error
GenMain(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenCall(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenEtc(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenConfig(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenLogic(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenServer(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenSvc(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error
GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, namingStyle NamingStyle) error
GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenEtc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenConfig(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenSvc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, cfg *conf.Config) error
}

View File

@@ -5,8 +5,10 @@ import (
"path/filepath"
"strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -18,10 +20,14 @@ Etcd:
Key: {{.serviceName}}.rpc
`
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetEtc()
serviceNameLower := formatFilename(ctx.GetMain().Base, namingStyle)
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", serviceNameLower))
etcFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", etcFilename))
text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate)
if err != nil {
@@ -29,6 +35,6 @@ func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, namingStyle Na
}
return util.With("etc").Parse(text).SaveTo(map[string]interface{}{
"serviceName": strings.ToLower(stringx.From(ctx.GetMain().Base).ToCamel()),
"serviceName": strings.ToLower(stringx.From(ctx.GetServiceName().Source()).ToCamel()),
}, fileName, false)
}

View File

@@ -6,8 +6,10 @@ import (
"strings"
"github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -46,10 +48,15 @@ func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
`
)
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetLogic()
for _, rpc := range proto.Service.RPC {
filename := filepath.Join(dir.Filename, formatFilename(rpc.Name+"_logic", namingStyle)+".go")
logicFilename, err := format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, logicFilename+".go")
functions, err := g.genLogicFunction(proto.PbPackage, rpc)
if err != nil {
return err

View File

@@ -5,8 +5,10 @@ import (
"path/filepath"
"strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -45,10 +47,13 @@ func main() {
}
`
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error {
dir := ctx.GetMain()
serviceNameLower := formatFilename(ctx.GetMain().Base, namingStyle)
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.go", serviceNameLower))
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
if err != nil {
return err
}
fileName := filepath.Join(ctx.GetMain().Filename, fmt.Sprintf("%v.go", mainFilename))
imports := make([]string, 0)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -63,7 +68,7 @@ func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, namingSty
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head,
"serviceName": strings.ToLower(stringx.From(ctx.GetMain().Base).ToCamel()),
"serviceName": strings.ToLower(ctx.GetServiceName().ToCamel()),
"imports": strings.Join(imports, util.NL),
"pkg": proto.PbPackage,
"serviceNew": stringx.From(proto.Service.Name).ToCamel(),

View File

@@ -5,11 +5,12 @@ import (
"path/filepath"
"strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config) error {
dir := ctx.GetPb()
cw := new(bytes.Buffer)
base := filepath.Dir(proto.Src)

View File

@@ -6,8 +6,10 @@ import (
"strings"
"github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
@@ -43,7 +45,7 @@ func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) (
`
)
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -54,7 +56,12 @@ func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, namingS
head := util.GetHead(proto.Name)
service := proto.Service
serverFile := filepath.Join(dir.Filename, formatFilename(service.Name+"_server", namingStyle)+".go")
serverFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name+"_server")
if err != nil {
return err
}
serverFile := filepath.Join(dir.Filename, serverFilename+".go")
funcList, err := g.genFunctions(proto.PbPackage, service)
if err != nil {
return err

View File

@@ -4,8 +4,10 @@ import (
"fmt"
"path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
)
const svcTemplate = `package svc
@@ -23,9 +25,14 @@ func NewServiceContext(c config.Config) *ServiceContext {
}
`
func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, namingStyle NamingStyle) error {
func (g *defaultGenerator) GenSvc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetSvc()
fileName := filepath.Join(dir.Filename, formatFilename("service_context", namingStyle)+".go")
svcFilename, err := format.FileNamingFormat(cfg.NamingFormat, "service_context")
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, svcFilename+".go")
text, err := util.LoadTemplate(category, svcTemplateFile, svcTemplate)
if err != nil {
return err

View File

@@ -33,6 +33,7 @@ type (
GetSvc() Dir
GetPb() Dir
GetMain() Dir
GetServiceName() stringx.String
}
Dir struct {
@@ -41,7 +42,8 @@ type (
Package string
}
defaultDirContext struct {
inner map[string]Dir
inner map[string]Dir
serviceName stringx.String
}
)
@@ -110,8 +112,10 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto) (DirContext, error) {
return nil, err
}
}
serviceName := strings.TrimSuffix(proto.Name, filepath.Ext(proto.Name))
return &defaultDirContext{
inner: inner,
inner: inner,
serviceName: stringx.From(strings.ReplaceAll(serviceName, "-", "")),
}, nil
}
@@ -151,6 +155,10 @@ func (d *defaultDirContext) GetMain() Dir {
return d.inner[wd]
}
func (d *defaultDirContext) GetServiceName() stringx.String {
return d.serviceName
}
func (d *Dir) Valid() bool {
return len(d.Filename) > 0 && len(d.Package) > 0
}

View File

@@ -1,24 +0,0 @@
package generator
type NamingStyle = string
const (
namingLower NamingStyle = "lower"
namingCamel NamingStyle = "camel"
namingSnake NamingStyle = "snake"
)
// IsNamingValid validates whether the namingStyle is valid or not,return
// namingStyle and true if it is valid, or else return empty string
// and false, and it is a valid value even namingStyle is empty string
func IsNamingValid(namingStyle string) (NamingStyle, bool) {
if len(namingStyle) == 0 {
namingStyle = namingLower
}
switch namingStyle {
case namingLower, namingCamel, namingSnake:
return namingStyle, true
default:
return "", false
}
}

View File

@@ -1,25 +0,0 @@
package generator
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNamingValid(t *testing.T) {
style, valid := IsNamingValid("")
assert.True(t, valid)
assert.Equal(t, namingLower, style)
_, valid = IsNamingValid("lower1")
assert.False(t, valid)
_, valid = IsNamingValid("lower")
assert.True(t, valid)
_, valid = IsNamingValid("snake")
assert.True(t, valid)
_, valid = IsNamingValid("camel")
assert.True(t, valid)
}

View File

@@ -8,13 +8,21 @@ import (
const goctlDir = ".goctl"
func GetTemplateDir(category string) (string, error) {
func GetGoctlHome() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, goctlDir), nil
}
return filepath.Join(home, goctlDir, category), nil
func GetTemplateDir(category string) (string, error) {
goctlHome, err := GetGoctlHome()
if err != nil {
return "", err
}
return filepath.Join(goctlHome, category), nil
}
func InitTemplates(category string, templates map[string]string) error {

View File

@@ -0,0 +1,155 @@
package format
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
)
const (
flagGo = "GO"
flagZero = "ZERO"
unknown style = iota
title
lower
upper
)
var ErrNamingFormat = errors.New("unsupported format")
type (
styleFormat struct {
before string
through string
after string
goStyle style
zeroStyle style
}
style int
)
// FileNamingFormat is used to format the file name. You can define the format style
// through the go and zero formatting characters. For example, you can define the snake
// format as go_zero, and the camel case format as goZero. You can even specify the split
// character, such as go#Zero, theoretically any combination can be used, but the prerequisite
// must meet the naming conventions of each operating system file name.
// Note: Formatting is based on snake or camel string
func FileNamingFormat(format, content string) (string, error) {
upperFormat := strings.ToUpper(format)
indexGo := strings.Index(upperFormat, flagGo)
indexZero := strings.Index(upperFormat, flagZero)
if indexGo < 0 || indexZero < 0 || indexGo > indexZero {
return "", ErrNamingFormat
}
var (
before, through, after string
flagGo, flagZero string
goStyle, zeroStyle style
err error
)
before = format[:indexGo]
flagGo = format[indexGo : indexGo+2]
through = format[indexGo+2 : indexZero]
flagZero = format[indexZero : indexZero+4]
after = format[indexZero+4:]
goStyle, err = getStyle(flagGo)
if err != nil {
return "", err
}
zeroStyle, err = getStyle(flagZero)
if err != nil {
return "", err
}
var formatStyle styleFormat
formatStyle.goStyle = goStyle
formatStyle.zeroStyle = zeroStyle
formatStyle.before = before
formatStyle.through = through
formatStyle.after = after
return doFormat(formatStyle, content)
}
func doFormat(f styleFormat, content string) (string, error) {
splits, err := split(content)
if err != nil {
return "", err
}
var join []string
for index, split := range splits {
if index == 0 {
join = append(join, transferTo(split, f.goStyle))
continue
}
join = append(join, transferTo(split, f.zeroStyle))
}
joined := strings.Join(join, f.through)
return f.before + joined + f.after, nil
}
func transferTo(in string, style style) string {
switch style {
case upper:
return strings.ToUpper(in)
case lower:
return strings.ToLower(in)
case title:
return strings.Title(in)
default:
return in
}
}
func split(content string) ([]string, error) {
var (
list []string
reader = strings.NewReader(content)
buffer = bytes.NewBuffer(nil)
)
for {
r, _, err := reader.ReadRune()
if err != nil {
if err == io.EOF {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
return list, nil
}
return nil, err
}
if r == '_' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
continue
}
if r >= 'A' && r <= 'Z' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
}
buffer.WriteRune(r)
}
}
func getStyle(flag string) (style, error) {
compare := strings.ToLower(flag)
switch flag {
case strings.ToLower(compare):
return lower, nil
case strings.ToUpper(compare):
return upper, nil
case strings.Title(compare):
return title, nil
default:
return unknown, fmt.Errorf("unexpected format: %s", flag)
}
}

View File

@@ -0,0 +1,112 @@
package format
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplit(t *testing.T) {
list, err := split("A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("goZero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "Zero"}, list)
list, err = split("Gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"Gozero"}, list)
list, err = split("go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "zero"}, list)
list, err = split("talGo_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"tal", "Go", "zero"}, list)
list, err = split("GOZERO")
assert.Nil(t, err)
assert.Equal(t, []string{"G", "O", "Z", "E", "R", "O"}, list)
list, err = split("gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"gozero"}, list)
list, err = split("")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("a_b_CD_EF")
assert.Nil(t, err)
assert.Equal(t, []string{"a", "b", "C", "D", "E", "F"}, list)
list, err = split("_")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("__")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("_A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("_A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("welcome_to_go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"welcome", "to", "go", "zero"}, list)
}
func TestFileNamingFormat(t *testing.T) {
testFileNamingFormat(t, "gozero", "welcome_to_go_zero", "welcometogozero")
testFileNamingFormat(t, "_go#zero_", "welcome_to_go_zero", "_welcome#to#go#zero_")
testFileNamingFormat(t, "Go#zero", "welcome_to_go_zero", "Welcome#to#go#zero")
testFileNamingFormat(t, "Go#Zero", "welcome_to_go_zero", "Welcome#To#Go#Zero")
testFileNamingFormat(t, "Go_Zero", "welcome_to_go_zero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "go_Zero", "welcome_to_go_zero", "welcome_To_Go_Zero")
testFileNamingFormat(t, "goZero", "welcome_to_go_zero", "welcomeToGoZero")
testFileNamingFormat(t, "GoZero", "welcome_to_go_zero", "WelcomeToGoZero")
testFileNamingFormat(t, "GOZero", "welcome_to_go_zero", "WELCOMEToGoZero")
testFileNamingFormat(t, "GoZERO", "welcome_to_go_zero", "WelcomeTOGOZERO")
testFileNamingFormat(t, "GOZERO", "welcome_to_go_zero", "WELCOMETOGOZERO")
testFileNamingFormat(t, "GO*ZERO", "welcome_to_go_zero", "WELCOME*TO*GO*ZERO")
testFileNamingFormat(t, "[GO#ZERO]", "welcome_to_go_zero", "[WELCOME#TO#GO#ZERO]")
testFileNamingFormat(t, "{go###zero}", "welcome_to_go_zero", "{welcome###to###go###zero}")
testFileNamingFormat(t, "{go###zerogo_zero}", "welcome_to_go_zero", "{welcome###to###go###zerogo_zero}")
testFileNamingFormat(t, "GogoZerozero", "welcome_to_go_zero", "WelcomegoTogoGogoZerozero")
testFileNamingFormat(t, "前缀GoZero后缀", "welcome_to_go_zero", "前缀WelcomeToGoZero后缀")
testFileNamingFormat(t, "GoZero", "welcometogozero", "Welcometogozero")
testFileNamingFormat(t, "GoZero", "WelcomeToGoZero", "WelcomeToGoZero")
testFileNamingFormat(t, "gozero", "WelcomeToGoZero", "welcometogozero")
testFileNamingFormat(t, "go_zero", "WelcomeToGoZero", "welcome_to_go_zero")
testFileNamingFormat(t, "Go_Zero", "WelcomeToGoZero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "Go_Zero", "", "")
testFileNamingFormatErr(t, "go", "")
testFileNamingFormatErr(t, "gOZero", "")
testFileNamingFormatErr(t, "zero", "")
testFileNamingFormatErr(t, "goZEro", "welcome_to_go_zero")
testFileNamingFormatErr(t, "goZERo", "welcome_to_go_zero")
testFileNamingFormatErr(t, "zerogo", "welcome_to_go_zero")
}
func testFileNamingFormat(t *testing.T, format, in, expected string) {
format, err := FileNamingFormat(format, in)
assert.Nil(t, err)
assert.Equal(t, expected, format)
}
func testFileNamingFormatErr(t *testing.T, format, in string) {
_, err := FileNamingFormat(format, in)
assert.Error(t, err)
}

View File

@@ -0,0 +1,41 @@
package name
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
type NamingStyle = string
const (
NamingLower NamingStyle = "lower"
NamingCamel NamingStyle = "camel"
NamingSnake NamingStyle = "snake"
)
// IsNamingValid validates whether the namingStyle is valid or not,return
// namingStyle and true if it is valid, or else return empty string
// and false, and it is a valid value even namingStyle is empty string
func IsNamingValid(namingStyle string) (NamingStyle, bool) {
if len(namingStyle) == 0 {
namingStyle = NamingLower
}
switch namingStyle {
case NamingLower, NamingCamel, NamingSnake:
return namingStyle, true
default:
return "", false
}
}
func FormatFilename(filename string, style NamingStyle) string {
switch style {
case NamingCamel:
return stringx.From(filename).ToCamel()
case NamingSnake:
return stringx.From(filename).ToSnake()
default:
return strings.ToLower(stringx.From(filename).ToCamel())
}
}

View File

@@ -0,0 +1,35 @@
package name
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNamingValid(t *testing.T) {
style, valid := IsNamingValid("")
assert.True(t, valid)
assert.Equal(t, NamingLower, style)
_, valid = IsNamingValid("lower1")
assert.False(t, valid)
_, valid = IsNamingValid("lower")
assert.True(t, valid)
_, valid = IsNamingValid("snake")
assert.True(t, valid)
_, valid = IsNamingValid("camel")
assert.True(t, valid)
}
func TestFormatFilename(t *testing.T) {
assert.Equal(t, "abc", FormatFilename("a_b_c", NamingLower))
assert.Equal(t, "ABC", FormatFilename("a_b_c", NamingCamel))
assert.Equal(t, "a_b_c", FormatFilename("a_b_c", NamingSnake))
assert.Equal(t, "a", FormatFilename("a", NamingSnake))
assert.Equal(t, "A", FormatFilename("a", NamingCamel))
// no flag to convert to snake
assert.Equal(t, "abc", FormatFilename("abc", NamingSnake))
}