Add goctl kotlin support

master
stevenzack 4 years ago committed by Kevin Wan
parent 4b636cd293
commit 926d746df5

@ -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

@ -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=

@ -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
}

@ -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
}

@ -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
}

@ -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,
},
},
},
{

Loading…
Cancel
Save