فهرست منبع

Add goctl kotlin support

stevenzack 4 سال پیش
والد
کامیت
926d746df5
6فایلهای تغییر یافته به همراه298 افزوده شده و 0 حذف شده
  1. 1 0
      go.mod
  2. 2 0
      go.sum
  3. 36 0
      tools/goctl/api/ktgen/cmd.go
  4. 67 0
      tools/goctl/api/ktgen/funcs.go
  5. 172 0
      tools/goctl/api/ktgen/gen.go
  6. 20 0
      tools/goctl/goctl.go

+ 1 - 0
go.mod

@@ -22,6 +22,7 @@ require (
 	github.com/google/uuid v1.1.1
 	github.com/gorilla/websocket v1.4.2 // indirect
 	github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
+	github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
 	github.com/justinas/alice v1.2.0
 	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
 	github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect

+ 2 - 0
go.sum

@@ -134,6 +134,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtg
 github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
 github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
+github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=

+ 36 - 0
tools/goctl/api/ktgen/cmd.go

@@ -0,0 +1,36 @@
+package ktgen
+
+import (
+	"errors"
+	"github.com/tal-tech/go-zero/core/lang"
+	"github.com/tal-tech/go-zero/tools/goctl/api/parser"
+	"github.com/urfave/cli"
+)
+
+func KtCommand(c *cli.Context) error {
+	apiFile := c.String("api")
+	if apiFile == "" {
+		return errors.New("missing -api")
+	}
+	dir := c.String("dir")
+	if dir == "" {
+		return errors.New("missing -dir")
+	}
+	pkg := c.String("pkg")
+	if pkg == "" {
+		return errors.New("missing -pkg")
+	}
+
+	p, e := parser.NewParser(apiFile)
+	if e != nil {
+		return e
+	}
+	api,e:=p.Parse()
+	if e!=nil {
+	    return e
+	}
+
+	lang.Must(genBase(dir,pkg,api))
+	lang.Must(genApi(dir,pkg, api))
+	return nil
+}

+ 67 - 0
tools/goctl/api/ktgen/funcs.go

@@ -0,0 +1,67 @@
+package ktgen
+
+import (
+	"github.com/tal-tech/go-zero/tools/goctl/api/util"
+	"log"
+	"strings"
+	"text/template"
+)
+var funcsMap=template.FuncMap{
+	"lowCamelCase":lowCamelCase,
+	"pathToFuncName":pathToFuncName,
+	"parseType":parseType,
+	"add":add,
+}
+func lowCamelCase(s string) string {
+	if len(s) < 1 {
+		return ""
+	}
+	s = util.ToCamelCase(util.ToSnakeCase(s))
+	return util.ToLower(s[:1]) + s[1:]
+}
+
+func pathToFuncName(path string) string {
+	if !strings.HasPrefix(path, "/") {
+		path = "/" + path
+	}
+
+	path = strings.Replace(path, "/", "_", -1)
+	path = strings.Replace(path, "-", "_", -1)
+
+	camel := util.ToCamelCase(path)
+	return util.ToLower(camel[:1]) + camel[1:]
+}
+func parseType(t string) string {
+	t=strings.Replace(t,"*","",-1)
+	if strings.HasPrefix(t,"[]"){
+		return "List<"+parseType(t[2:])+ ">"
+	}
+
+	if strings.HasPrefix(t,"map"){
+		tys,e:=util.DecomposeType(t)
+		if e!=nil{
+		    log.Fatal(e)
+		}
+		if len(tys)!=2{
+			log.Fatal("Map type number !=2")
+		}
+		return "Map<String,"+parseType(tys[1])+">"
+	}
+
+	switch t {
+	case "string":
+		return "String"
+	case "int", "int32", "int64":
+		return "Int"
+	case "float", "float32", "float64":
+		return "Double"
+	case "bool":
+		return "Boolean"
+	default:
+		return t
+	}
+}
+
+func add(a,i int)int{
+	return a+i
+}

+ 172 - 0
tools/goctl/api/ktgen/gen.go

@@ -0,0 +1,172 @@
+package ktgen
+
+import (
+	"github.com/tal-tech/go-zero/core/logx"
+	"github.com/tal-tech/go-zero/tools/goctl/api/spec"
+	"log"
+	"os"
+	"path/filepath"
+	"text/template"
+	"github.com/iancoleman/strcase"
+)
+
+const (
+	apiBaseTemplate = `package {{.}}
+
+import com.google.gson.Gson
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.io.BufferedReader
+import java.io.InputStreamReader
+import java.io.OutputStreamWriter
+import java.net.HttpURLConnection
+import java.net.URL
+
+const val SERVER = "http://localhost:8080"
+
+suspend fun apiPost(
+    uri: String,
+    body: Any,
+    onOk: ((String) -> Unit)? = null,
+    onFail: ((String) -> Unit)? = null,
+    eventually: (() -> Unit)? = null
+) = withContext(Dispatchers.IO) {
+    val url = URL(SERVER + uri)
+    with(url.openConnection() as HttpURLConnection) {
+        requestMethod = "POST"
+        headerFields["Content-Type"] = listOf("Application/json")
+
+        val data = when (body) {
+            is String -> {
+                body
+            }
+            else -> {
+                Gson().toJson(body)
+            }
+        }
+        val wr = OutputStreamWriter(outputStream)
+        wr.write(data)
+        wr.flush()
+
+        //response
+        BufferedReader(InputStreamReader(inputStream)).use {
+            val response = it.readText()
+            if (responseCode == 200) {
+                onOk?.invoke(response)
+            } else {
+                onFail?.invoke(response)
+            }
+        }
+    }
+    eventually?.invoke()
+}
+
+suspend fun apiGet(
+    uri: String,
+    onOk: ((String) -> Unit)? = null,
+    onFail: ((String) -> Unit)? = null,
+    eventually: (() -> Unit)? = null
+) = withContext(Dispatchers.IO) {
+    val url = URL(SERVER + uri)
+    with(url.openConnection() as HttpURLConnection) {
+        requestMethod = "POST"
+        headerFields["Content-Type"] = listOf("Application/json")
+
+        val wr = OutputStreamWriter(outputStream)
+        wr.flush()
+
+        //response
+        BufferedReader(InputStreamReader(inputStream)).use {
+            val response = it.readText()
+            if (responseCode == 200) {
+                onOk?.invoke(response)
+            } else {
+                onFail?.invoke(response)
+            }
+        }
+    }
+    eventually?.invoke()
+}
+`
+	apiTemplate = `package {{with .Info}}{{.Title}}{{end}}
+
+import com.google.gson.Gson
+
+object Api{
+	{{range .Types}}
+	data class {{.Name}}({{$length := (len .Members)}}{{range $i,$item := .Members}}
+		val {{with $item}}{{lowCamelCase .Name}}: {{parseType .Type}}{{end}}{{if ne $i (add $length -1)}},{{end}}{{end}}
+	){{end}}
+	{{with .Service}}
+	{{range .Routes}}suspend fun {{pathToFuncName .Path}}({{if ne .Method "get"}}
+		req:{{with .RequestType}}{{.Name}},{{end}}{{end}}
+		onOk: (({{with .ResponseType}}{{.Name}}{{end}}) -> Unit)? = null,
+        onFail: ((String) -> Unit)? = null,
+        eventually: (() -> Unit)? = null
+    ){
+        api{{if eq .Method "get"}}Get{{else}}Post{{end}}("{{.Path}}",{{if ne .Method "get"}}req,{{end}} onOk = {
+            onOk?.invoke(Gson().fromJson(it,{{with .ResponseType}}{{.Name}}{{end}}::class.java))
+        }, onFail = onFail, eventually =eventually)
+    }
+	{{end}}{{end}}
+}`
+)
+
+func genBase(dir, pkg string, api *spec.ApiSpec) error {
+	e := os.MkdirAll(dir, 0755)
+	if e != nil {
+		logx.Error(e)
+		return e
+	}
+	path := filepath.Join(dir, "BaseApi.kt")
+	if _, e := os.Stat(path); e == nil {
+		return nil
+	}
+
+	file, e := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+	if e != nil {
+		logx.Error(e)
+		return e
+	}
+	defer file.Close()
+
+	t, e := template.New("n").Parse(apiBaseTemplate)
+	if e != nil {
+		logx.Error(e)
+		return e
+	}
+	e = t.Execute(file, pkg)
+	if e != nil {
+		logx.Error(e)
+		return e
+	}
+	return nil
+}
+
+func genApi(dir, pkg string, api *spec.ApiSpec) error {
+	path := filepath.Join(dir, strcase.ToCamel(api.Info.Title+"Api")+".kt")
+	api.Info.Title= pkg
+
+	e:=os.MkdirAll(dir,0755)
+	if e!=nil {
+	    logx.Error(e)
+	    return e
+	}
+
+	file,e:=os.OpenFile(path,os.O_WRONLY|os.O_TRUNC|os.O_CREATE,0644)
+	if e!=nil {
+	    logx.Error(e)
+	    return e
+	}
+	defer file.Close()
+
+	t,e:=template.New("api").Funcs(funcsMap).Parse(apiTemplate)
+	if e!=nil{
+	    log.Fatal(e)
+	}
+	e=t.Execute(file,api)
+	if e!=nil{
+	    log.Fatal(e)
+	}
+	return nil
+}

+ 20 - 0
tools/goctl/goctl.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	"fmt"
+	"github.com/tal-tech/go-zero/tools/goctl/api/ktgen"
 	"os"
 
 	"github.com/tal-tech/go-zero/core/logx"
@@ -150,6 +151,25 @@ var (
 					},
 					Action: dartgen.DartCommand,
 				},
+				{
+					Name:  "kt",
+					Usage: "generate kotlin code for provided api file",
+					Flags: []cli.Flag{
+						cli.StringFlag{
+							Name:  "dir",
+							Usage: "the target directory",
+						},
+						cli.StringFlag{
+							Name:  "api",
+							Usage: "the api file",
+						},
+						cli.StringFlag{
+							Name:  "pkg",
+							Usage: "define package name for kotlin file",
+						},
+					},
+					Action: ktgen.KtCommand,
+				},
 			},
 		},
 		{