goctl added

This commit is contained in:
kim
2020-07-29 17:11:41 +08:00
parent b1975d29a7
commit 121323b8c3
142 changed files with 10690 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
package gogen
import (
"errors"
"fmt"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"zero/core/lang"
apiformat "zero/tools/goctl/api/format"
"zero/tools/goctl/api/parser"
apiutil "zero/tools/goctl/api/util"
"zero/tools/goctl/util"
"github.com/logrusorgru/aurora"
"github.com/urfave/cli"
)
const tmpFile = "%s-%d"
var tmpDir = path.Join(os.TempDir(), "goctl")
func GoCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")
if len(apiFile) == 0 {
return errors.New("missing -api")
}
if len(dir) == 0 {
return errors.New("missing -dir")
}
p, err := parser.NewParser(apiFile)
if err != nil {
return err
}
api, err := p.Parse()
if err != nil {
return err
}
lang.Must(util.MkdirIfNotExist(dir))
lang.Must(genEtc(dir, api))
lang.Must(genConfig(dir, api))
lang.Must(genMain(dir, api))
lang.Must(genServiceContext(dir, api))
lang.Must(genTypes(dir, api))
lang.Must(genHandlers(dir, api))
lang.Must(genRoutes(dir, api))
lang.Must(genLogic(dir, api))
// it does not work
format(dir)
if err := backupAndSweep(apiFile); err != nil {
return err
}
if err = apiformat.ApiFormat(apiFile, false); err != nil {
return err
}
fmt.Println(aurora.Green("Done."))
return nil
}
func backupAndSweep(apiFile string) error {
var err error
var wg sync.WaitGroup
wg.Add(2)
_ = os.MkdirAll(tmpDir, os.ModePerm)
go func() {
_, fileName := filepath.Split(apiFile)
_, e := apiutil.Copy(apiFile, fmt.Sprintf(path.Join(tmpDir, tmpFile), fileName, time.Now().Unix()))
if e != nil {
err = e
}
wg.Done()
}()
go func() {
if e := sweep(); e != nil {
err = e
}
wg.Done()
}()
wg.Wait()
return err
}
func format(dir string) {
cmd := exec.Command("go", "fmt", "./"+dir+"...")
_, err := cmd.CombinedOutput()
if err != nil {
print(err.Error())
}
}
func sweep() error {
keepTime := time.Now().AddDate(0, 0, -7)
return filepath.Walk(tmpDir, func(fpath string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
pos := strings.LastIndexByte(info.Name(), '-')
if pos > 0 {
timestamp := info.Name()[pos+1:]
seconds, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
// print error and ignore
fmt.Println(aurora.Red(fmt.Sprintf("sweep ignored file: %s", fpath)))
return nil
}
tm := time.Unix(seconds, 0)
if tm.Before(keepTime) {
if err := os.Remove(fpath); err != nil {
fmt.Println(aurora.Red(fmt.Sprintf("failed to remove file: %s", fpath)))
return err
}
}
}
return nil
})
}

View File

@@ -0,0 +1,48 @@
package gogen
import (
"bytes"
"text/template"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
)
const (
configFile = "config.go"
configTemplate = `package config
import (
"zero/ngin"
{{.authImport}}
)
type Config struct {
ngin.NgConf
}
`
)
func genConfig(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, configDir, configFile)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
var authImportStr = ""
t := template.Must(template.New("configTemplate").Parse(configTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"authImport": authImportStr,
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}

View File

@@ -0,0 +1,56 @@
package gogen
import (
"bytes"
"fmt"
"strconv"
"text/template"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
)
const (
defaultPort = 8888
etcDir = "etc"
etcTemplate = `{
"Name": "{{.serviceName}}",
"Host": "{{.host}}",
"Port": {{.port}}
}`
)
func genEtc(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.json", api.Service.Name))
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
service := api.Service
host, ok := util.GetAnnotationValue(service.Annotations, "server", "host")
if !ok {
host = "0.0.0.0"
}
port, ok := util.GetAnnotationValue(service.Annotations, "server", "port")
if !ok {
port = strconv.Itoa(defaultPort)
}
t := template.Must(template.New("etcTemplate").Parse(etcTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"serviceName": service.Name,
"host": host,
"port": port,
})
if err != nil {
return err
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}

View File

@@ -0,0 +1,199 @@
package gogen
import (
"bytes"
"fmt"
"path"
"sort"
"strings"
"text/template"
"zero/tools/goctl/api/spec"
apiutil "zero/tools/goctl/api/util"
"zero/tools/goctl/util"
)
const (
handlerTemplate = `package handler
import (
"net/http"
{{.importPackages}}
)
func {{.handlerName}}(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
l := logic.{{.logic}}(r.Context(), ctx)
{{.handlerBody}}
}
}
`
handlerBodyTemplate = `{{.parseRequest}}
{{.processBody}}
`
parseRequestTemplate = `var req {{.requestType}}
if err := httpx.Parse(r, &req); err != nil {
logx.Error(err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
`
hasRespTemplate = `
{{.logicResponse}} l.{{.callee}}({{.req}})
// TODO write data to response
`
)
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)
var reqBody string
if len(route.RequestType.Name) > 0 {
var bodyBuilder strings.Builder
t := template.Must(template.New("parseRequest").Parse(parseRequestTemplate))
if err := t.Execute(&bodyBuilder, map[string]string{
"requestType": typesPacket + "." + util.Title(route.RequestType.Name),
}); err != nil {
return err
}
reqBody = bodyBuilder.String()
}
var req = "req"
if len(route.RequestType.Name) == 0 {
req = ""
}
var logicResponse = ""
var writeResponse = "nil, nil"
if len(route.ResponseType.Name) > 0 {
logicResponse = "resp, err :="
writeResponse = "resp, err"
} else {
logicResponse = "err :="
writeResponse = "nil, err"
}
var logicBodyBuilder strings.Builder
t := template.Must(template.New("hasRespTemplate").Parse(hasRespTemplate))
if err := t.Execute(&logicBodyBuilder, map[string]string{
"callee": strings.Title(strings.TrimSuffix(handler, "Handler")),
"req": req,
"logicResponse": logicResponse,
"writeResponse": writeResponse,
}); err != nil {
return err
}
respBody := logicBodyBuilder.String()
if !strings.HasSuffix(handler, "Handler") {
handler = handler + "Handler"
}
var bodyBuilder strings.Builder
bodyTemplate := template.Must(template.New("handlerBodyTemplate").Parse(handlerBodyTemplate))
if err := bodyTemplate.Execute(&bodyBuilder, map[string]string{
"parseRequest": reqBody,
"processBody": respBody,
}); err != nil {
return err
}
return doGenToFile(dir, handler, group, route, bodyBuilder)
}
func doGenToFile(dir, handler string, group spec.Group, route spec.Route, bodyBuilder strings.Builder) error {
if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler)
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
filename := strings.ToLower(handler)
if strings.HasSuffix(filename, "handler") {
filename = filename + ".go"
} else {
filename = filename + "handler.go"
}
fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
t := template.Must(template.New("handlerTemplate").Parse(handlerTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"logic": "New" + strings.TrimSuffix(strings.Title(handler), "Handler") + "Logic",
"importPackages": genHandlerImports(group, route, parentPkg),
"handlerName": handler,
"handlerBody": strings.TrimSpace(bodyBuilder.String()),
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}
func genHandlers(dir string, api *spec.ApiSpec) error {
for _, group := range api.Service.Groups {
for _, route := range group.Routes {
if err := genHandler(dir, group, route); err != nil {
return err
}
}
}
return nil
}
func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) string {
var imports []string
if len(route.RequestType.Name) > 0 || len(route.ResponseType.Name) > 0 {
imports = append(imports, "\"zero/core/httpx\"")
}
if len(route.RequestType.Name) > 0 {
imports = append(imports, "\"zero/core/logx\"")
}
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, contextDir)))
if len(route.RequestType.Name) > 0 || len(route.ResponseType.Name) > 0 {
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, typesDir)))
}
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, getLogicFolderPath(group, route))))
sort.Strings(imports)
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", "")
}
return handlerName
}
func getHandlerFolderPath(group spec.Group, route spec.Route) string {
folder, ok := apiutil.GetAnnotationValue(route.Annotations, "server", folderProperty)
if !ok {
folder, ok = apiutil.GetAnnotationValue(group.Annotations, "server", folderProperty)
if !ok {
return handlerDir
}
}
folder = strings.TrimPrefix(folder, "/")
folder = strings.TrimSuffix(folder, "/")
return path.Join(handlerDir, folder)
}
func getHandlerName(handler string) string {
return getHandlerBaseName(handler) + "Handler"
}

View File

@@ -0,0 +1,130 @@
package gogen
import (
"bytes"
"fmt"
"path"
"strings"
"text/template"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
)
const logicTemplate = `package logic
import (
{{.imports}}
)
type {{.logic}} struct {
ctx context.Context
logx.Logger
}
func New{{.logic}}(ctx context.Context, svcCtx *svc.ServiceContext) {{.logic}} {
return {{.logic}}{
ctx: ctx,
Logger: logx.WithContext(ctx),
}
// TODO need set model here from svc
}
func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
{{.returnString}}
}
`
func genLogic(dir string, api *spec.ApiSpec) error {
for _, g := range api.Service.Groups {
for _, r := range g.Routes {
err := genLogicByRoute(dir, g, r)
if err != nil {
return err
}
}
}
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)
}
handler = strings.TrimSuffix(handler, "handler")
handler = strings.TrimSuffix(handler, "Handler")
filename := strings.ToLower(handler)
goFile := filename + "logic.go"
fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
imports := genLogicImports(route, parentPkg)
responseString := ""
returnString := ""
requestString := ""
if len(route.ResponseType.Name) > 0 {
responseString = "(*types." + strings.Title(route.ResponseType.Name) + ", error)"
returnString = "return nil, nil"
} else {
responseString = "error"
returnString = "return nil"
}
if len(route.RequestType.Name) > 0 {
requestString = "req " + "types." + strings.Title(route.RequestType.Name)
}
t := template.Must(template.New("logicTemplate").Parse(logicTemplate))
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")),
"responseType": responseString,
"returnString": returnString,
"request": requestString,
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}
func getLogicFolderPath(group spec.Group, route spec.Route) string {
folder, ok := util.GetAnnotationValue(route.Annotations, "server", folderProperty)
if !ok {
folder, ok = util.GetAnnotationValue(group.Annotations, "server", folderProperty)
if !ok {
return logicDir
}
}
folder = strings.TrimPrefix(folder, "/")
folder = strings.TrimSuffix(folder, "/")
return path.Join(logicDir, folder)
}
func genLogicImports(route spec.Route, parentPkg string) string {
var imports []string
imports = append(imports, `"context"`)
imports = append(imports, "\n")
imports = append(imports, `"zero/core/logx"`)
if len(route.ResponseType.Name) > 0 || len(route.RequestType.Name) > 0 {
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, typesDir)))
}
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, contextDir)))
return strings.Join(imports, "\n\t")
}

View File

@@ -0,0 +1,85 @@
package gogen
import (
"bytes"
"fmt"
"path"
"sort"
"strings"
"text/template"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
)
const mainTemplate = `package main
import (
"flag"
{{.importPackages}}
)
var configFile = flag.String("f", "etc/{{.serviceName}}.json", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
engine := ngin.MustNewEngine(c.NgConf)
defer engine.Stop()
handler.RegisterHandlers(engine, ctx)
engine.Start()
}
`
func genMain(dir string, api *spec.ApiSpec) error {
name := strings.ToLower(api.Service.Name)
if strings.HasSuffix(name, "-api") {
name = strings.ReplaceAll(name, "-api", "")
}
goFile := name + ".go"
fp, created, err := util.MaybeCreateFile(dir, "", goFile)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
t := template.Must(template.New("mainTemplate").Parse(mainTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"importPackages": genMainImports(parentPkg),
"serviceName": api.Service.Name,
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}
func genMainImports(parentPkg string) string {
imports := []string{
`"zero/core/conf"`,
`"zero/ngin"`,
}
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, configDir)))
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, handlerDir)))
imports = append(imports, fmt.Sprintf("\"%s\"", path.Join(parentPkg, contextDir)))
sort.Strings(imports)
return strings.Join(imports, "\n\t")
}

View File

@@ -0,0 +1,193 @@
package gogen
import (
"bytes"
"errors"
"fmt"
"path"
"sort"
"strings"
"text/template"
"zero/core/collection"
"zero/tools/goctl/api/spec"
apiutil "zero/tools/goctl/api/util"
"zero/tools/goctl/util"
)
const (
routesFilename = "routes.go"
routesTemplate = `// DO NOT EDIT, generated by goctl
package handler
import (
"net/http"
{{.importPackages}}
)
func RegisterHandlers(engine *ngin.Engine, serverCtx *svc.ServiceContext) {
{{.routesAdditions}}
}
`
routesAdditionTemplate = `
engine.AddRoutes([]ngin.Route{
{{.routes}}
}{{.jwt}}{{.signature}})
`
)
var mapping = map[string]string{
"delete": "http.MethodDelete",
"get": "http.MethodGet",
"head": "http.MethodHead",
"post": "http.MethodPost",
"put": "http.MethodPut",
}
type (
group struct {
routes []route
jwtEnabled bool
signatureEnabled bool
authName string
}
route struct {
method string
path string
handler string
}
)
func genRoutes(dir string, api *spec.ApiSpec) error {
var builder strings.Builder
groups, err := getRoutes(api)
if err != nil {
return err
}
gt := template.Must(template.New("groupTemplate").Parse(routesAdditionTemplate))
for _, g := range groups {
var gbuilder strings.Builder
for _, r := range g.routes {
fmt.Fprintf(&gbuilder, `
{
Method: %s,
Path: "%s",
Handler: %s,
},`,
r.method, r.path, r.handler)
}
jwt := ""
if g.jwtEnabled {
jwt = fmt.Sprintf(", ngin.WithJwt(serverCtx.Config.%s.AccessSecret)", g.authName)
}
signature := ""
if g.signatureEnabled {
signature = fmt.Sprintf(", ngin.WithSignature(serverCtx.Config.%s.Signature)", g.authName)
}
if err := gt.Execute(&builder, map[string]string{
"routes": strings.TrimSpace(gbuilder.String()),
"jwt": jwt,
"signature": signature,
}); err != nil {
return err
}
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
filename := path.Join(dir, handlerDir, routesFilename)
if err := util.RemoveOrQuit(filename); err != nil {
return err
}
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
t := template.Must(template.New("routesTemplate").Parse(routesTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"importPackages": genRouteImports(parentPkg, api),
"routesAdditions": strings.TrimSpace(builder.String()),
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}
func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
var importSet = collection.NewSet()
importSet.AddStr(`"zero/ngin"`)
importSet.AddStr(fmt.Sprintf("\"%s\"", path.Join(parentPkg, contextDir)))
for _, group := range api.Service.Groups {
for _, route := range group.Routes {
folder, ok := apiutil.GetAnnotationValue(route.Annotations, "server", folderProperty)
if !ok {
folder, ok = apiutil.GetAnnotationValue(group.Annotations, "server", folderProperty)
if !ok {
continue
}
}
importSet.AddStr(fmt.Sprintf("%s \"%s\"", folder, path.Join(parentPkg, handlerDir, folder)))
}
}
imports := importSet.KeysStr()
sort.Strings(imports)
return strings.Join(imports, "\n\t")
}
func getRoutes(api *spec.ApiSpec) ([]group, error) {
var routes []group
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)"
folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", folderProperty)
if ok {
handler = folder + "." + strings.ToUpper(handler[:1]) + handler[1:]
} else {
folder, ok = apiutil.GetAnnotationValue(g.Annotations, "server", folderProperty)
if ok {
handler = folder + "." + strings.ToUpper(handler[:1]) + handler[1:]
}
}
groupedRoutes.routes = append(groupedRoutes.routes, route{
method: mapping[r.Method],
path: r.Path,
handler: handler,
})
}
if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "jwt"); ok {
groupedRoutes.authName = value
groupedRoutes.jwtEnabled = true
}
if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "signature"); ok {
if groupedRoutes.authName != "" && groupedRoutes.authName != value {
return nil, errors.New("auth signature should config same")
}
groupedRoutes.signatureEnabled = true
}
routes = append(routes, groupedRoutes)
}
return routes, nil
}

View File

@@ -0,0 +1,63 @@
package gogen
import (
"bytes"
"fmt"
"path"
"text/template"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
)
const (
contextFilename = "servicecontext.go"
contextTemplate = `package svc
import {{.configImport}}
type ServiceContext struct {
Config {{.config}}
}
func NewServiceContext(config {{.config}}) *ServiceContext {
return &ServiceContext{Config: config}
}
`
)
func genServiceContext(dir string, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, contextDir, contextFilename)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
var authNames = getAuths(api)
var auths []string
for _, item := range authNames {
auths = append(auths, fmt.Sprintf("%s config.AuthConfig", item))
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
var configImport = "\"" + path.Join(parentPkg, configDir) + "\""
t := template.Must(template.New("contextTemplate").Parse(contextTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]string{
"configImport": configImport,
"config": "config.Config",
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}

View File

@@ -0,0 +1,146 @@
package gogen
import (
"bytes"
"errors"
"fmt"
"io"
"path"
"strings"
"text/template"
"zero/tools/goctl/api/spec"
apiutil "zero/tools/goctl/api/util"
"zero/tools/goctl/util"
)
const (
typesFile = "types.go"
typesTemplate = `// DO NOT EDIT, generated by goctl
package types{{if .containsTime}}
import (
"time"
){{end}}
{{.types}}
`
)
func BuildTypes(types []spec.Type) (string, error) {
var builder strings.Builder
first := true
for _, tp := range types {
if first {
first = false
} else {
builder.WriteString("\n\n")
}
if err := writeType(&builder, tp, types); err != nil {
return "", apiutil.WrapErr(err, "Type "+tp.Name+" generate error")
}
}
return builder.String(), nil
}
func genTypes(dir string, api *spec.ApiSpec) error {
val, err := BuildTypes(api.Types)
if err != nil {
return err
}
filename := path.Join(dir, typesDir, typesFile)
if err := util.RemoveOrQuit(filename); err != nil {
return err
}
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
if err != nil {
return err
}
if !created {
return nil
}
defer fp.Close()
t := template.Must(template.New("typesTemplate").Parse(typesTemplate))
buffer := new(bytes.Buffer)
err = t.Execute(buffer, map[string]interface{}{
"types": val,
"containsTime": api.ContainsTime(),
})
if err != nil {
return nil
}
formatCode := formatCode(buffer.String())
_, err = fp.WriteString(formatCode)
return err
}
func convertTypeCase(types []spec.Type, t string) (string, error) {
ts, err := apiutil.DecomposeType(t)
if err != nil {
return "", err
}
var defTypes []string
for _, tp := range ts {
for _, typ := range types {
if typ.Name == tp {
defTypes = append(defTypes, tp)
}
if len(typ.Annotations) > 0 {
if value, ok := apiutil.GetAnnotationValue(typ.Annotations, "serverReplacer", tp); ok {
t = strings.ReplaceAll(t, tp, value)
}
}
}
}
for _, tp := range defTypes {
t = strings.ReplaceAll(t, tp, util.Title(tp))
}
return t, nil
}
func writeType(writer io.Writer, tp spec.Type, types []spec.Type) error {
fmt.Fprintf(writer, "type %s struct {\n", util.Title(tp.Name))
for _, member := range tp.Members {
if member.IsInline {
var found = false
for _, ty := range types {
if strings.ToLower(ty.Name) == strings.ToLower(member.Name) {
found = true
}
}
if !found {
return errors.New("inline type " + member.Name + " not exist, please correct api file")
}
if _, err := fmt.Fprintf(writer, "%s\n", strings.Title(member.Type)); err != nil {
return err
} else {
continue
}
}
tpString, err := convertTypeCase(types, member.Type)
if err != nil {
return err
}
pm, err := member.GetPropertyName()
if err != nil {
return err
}
if !strings.Contains(pm, "_") {
if strings.Title(member.Name) != strings.Title(pm) {
fmt.Printf("type: %s, property name %s json tag illegal, "+
"should set json tag as `json:\"%s\"` \n", tp.Name, member.Name, util.Untitle(member.Name))
}
}
if err := writeProperty(writer, member.Name, tpString, member.Tag, member.GetComment(), 1); err != nil {
return err
}
}
fmt.Fprintf(writer, "}")
return nil
}

View File

@@ -0,0 +1,67 @@
package gogen
import (
"fmt"
goformat "go/format"
"io"
"path/filepath"
"strings"
"zero/core/collection"
"zero/tools/goctl/api/spec"
"zero/tools/goctl/api/util"
"zero/tools/goctl/vars"
)
func getParentPackage(dir string) (string, error) {
absDir, err := filepath.Abs(dir)
if err != nil {
return "", err
}
pos := strings.Index(absDir, vars.ProjectName)
if pos < 0 {
return "", fmt.Errorf("%s not in project directory", dir)
}
return absDir[pos:], nil
}
func writeIndent(writer io.Writer, indent int) {
for i := 0; i < indent; i++ {
fmt.Fprint(writer, "\t")
}
}
func writeProperty(writer io.Writer, name, tp, tag, comment string, indent int) error {
writeIndent(writer, indent)
var err error
if len(comment) > 0 {
comment = strings.TrimPrefix(comment, "//")
comment = "//" + comment
_, err = fmt.Fprintf(writer, "%s %s %s %s\n", strings.Title(name), tp, tag, comment)
} else {
_, err = fmt.Fprintf(writer, "%s %s %s\n", strings.Title(name), tp, tag)
}
return err
}
func getAuths(api *spec.ApiSpec) []string {
var authNames = collection.NewSet()
for _, g := range api.Service.Groups {
if value, ok := util.GetAnnotationValue(g.Annotations, "server", "jwt"); ok {
authNames.Add(value)
}
if value, ok := util.GetAnnotationValue(g.Annotations, "server", "signature"); ok {
authNames.Add(value)
}
}
return authNames.KeysStr()
}
func formatCode(code string) string {
ret, err := goformat.Source([]byte(code))
if err != nil {
return code
}
return string(ret)
}

View File

@@ -0,0 +1,12 @@
package gogen
const (
interval = "internal/"
typesPacket = "types"
configDir = interval + "config"
contextDir = interval + "svc"
handlerDir = interval + "handler"
logicDir = interval + "logic"
typesDir = interval + typesPacket
folderProperty = "folder"
)