2021-03-02 06:37:49 -03:00

244 lines
5.3 KiB
Go

//go:generate sh generate.sh
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"strings"
"text/template"
)
// Method is a method on the Gitpod Server interface
type Method struct {
Name string `json:"name"`
GoName string `json:"-"`
Params []struct {
Name string `json:"name"`
Type string `json:"type"`
GoName string `json:"-"`
GoType string `json:"-"`
}
GoParams string
Result string `json:"result"`
GoResultType string `json:"-"`
GoReturnType string `json:"-"`
Void bool `json:"-"`
PointerReturn bool `json:"-"`
}
const tpl = `package rpc
// GitpodServerInterface wraps the Gitpod server
type APIInterface interface {
{{- range $m := . }}
{{ .GoName }}({{ .GoParams }}) {{ .GoReturnType }}
{{- end }}
}
// FunctionName is the name of an RPC function
type FunctionName string
const (
{{- range $m := . }}
// Function{{ .GoName }} is the name of the {{ .Name }} function
Function{{ .GoName }} FunctionName = "{{ .Name }}"
{{- end }}
)
// APIoverJSONRPC makes JSON RPC calls to the Gitpod server
type APIoverJSONRPC struct {
C *jsonrpc2.Conn
}
{{- range $m := . }}
// {{ .GoName }} calls {{ .Name }} on the server
func (gp *APIoverJSONRPC) {{ .GoName }}({{ .GoParams }}) {{ .GoReturnType }} {
var _params []interface{}
{{ range $p := .Params }}
_params = append(_params, {{ .GoName }})
{{- end }}
{{ if .Void }}
err = gp.C.Call(ctx, "{{ $m.Name }}", _params, nil)
if err != nil {
return
}
{{ else }}
var result {{ .GoResultType }}
err = gp.C.Call(ctx, "{{ $m.Name }}", _params, &result)
if err != nil {
return
}
res = {{ if .PointerReturn }}&{{end}}result
{{ end }}
return
}
{{ end }}
`
func main() {
var mths []Method
err := json.NewDecoder(os.Stdin).Decode(&mths)
if err != nil {
log.Fatal(err)
}
out, err := os.OpenFile("gitpod-service.go", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer out.Close()
allUsedTSTypes := make(map[string]string)
for i, m := range mths {
mths[i].GoName = strings.ToUpper(m.Name[0:1]) + m.Name[1:]
rt := typescriptTypeToGoType(m.Result)
if rt == "void" {
mths[i].GoReturnType = "(err error)"
mths[i].Void = true
} else {
mths[i].GoResultType = strings.TrimPrefix(rt, "*")
mths[i].GoReturnType = fmt.Sprintf("(res %s, err error)", rt)
mths[i].PointerReturn = !strings.HasPrefix(rt, "[]") && rt != "string"
}
if strings.HasPrefix(rt, "[]") || strings.HasPrefix(rt, "*") {
tt := strings.TrimSuffix(trimTypescriptAdornments(m.Result), "[]")
gt := strings.TrimPrefix(rt, "[]")
gt = strings.TrimPrefix(gt, "*")
allUsedTSTypes[tt] = gt
}
var goParams []string
goParams = append(goParams, "ctx context.Context")
for j, p := range m.Params {
gt := typescriptTypeToGoType(p.Type)
mths[i].Params[j].GoName = p.Name
mths[i].Params[j].GoType = gt
goParams = append(goParams, fmt.Sprintf("%s %s", mths[i].Params[j].GoName, mths[i].Params[j].GoType))
if strings.HasPrefix(gt, "[]") || strings.HasPrefix(gt, "*") {
tt := strings.TrimSuffix(trimTypescriptAdornments(p.Type), "[]")
gt = strings.TrimPrefix(gt, "[]")
gt = strings.TrimPrefix(gt, "*")
allUsedTSTypes[tt] = gt
}
}
mths[i].GoParams = strings.Join(goParams, ", ")
}
tmpl, err := template.New("tpl").Parse(tpl)
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(out, mths)
if err != nil {
log.Fatal(err)
}
tmpfile, err := os.CreateTemp("", "")
if err != nil {
log.Fatal(err)
}
tmpfile.Close()
fmt.Println("tmp file: " + tmpfile.Name())
// defer os.Remove(tmpfile.Name())
for tst, gt := range allUsedTSTypes {
if _, bt := baseTypeMap[tst]; bt {
continue
}
fmt.Println("generating " + tst)
cmd := exec.Command("yarn", "typescript-json-schema", "tsconfig.json", "-o", tmpfile.Name(), tst)
cmd.Dir = ".."
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
buf := bytes.NewBuffer(nil)
cmd = exec.Command("schema-generate", tmpfile.Name())
cmd.Stdout = buf
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal(err)
}
lines := strings.Split(buf.String(), "\n")
for _, l := range lines {
var hasPrefix bool
for _, p := range []string{
"//",
"package main",
"type Root string",
"import (",
" \"encoding",
" \"bytes",
" \"fmt",
")",
} {
if strings.HasPrefix(l, p) {
hasPrefix = true
break
}
}
if hasPrefix {
continue
}
if strings.HasPrefix(l, "type Root struct {") {
l = fmt.Sprintf("type %s struct {", gt)
}
fmt.Fprintln(out, l)
}
}
}
var baseTypeMap = map[string]string{
"string": "string",
"boolean": "bool",
"number": "float32",
"void": "void",
}
func trimTypescriptAdornments(t string) string {
res := t
if strings.HasPrefix(res, "Promise<") {
res = strings.TrimSuffix(strings.TrimPrefix(res, "Promise<"), ">")
}
if strings.HasPrefix(res, "Partial<") {
res = strings.TrimSuffix(strings.TrimPrefix(res, "Partial<"), ">")
}
res = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(res), "| undefined"))
return res
}
func typescriptTypeToGoType(ts string) (res string) {
res = trimTypescriptAdornments(ts)
nt, ok := baseTypeMap[res]
if ok {
res = nt
} else {
res = "*" + res
}
if strings.HasSuffix(res, "[]") {
res = "[]" + strings.TrimSuffix(res, "[]")
}
res = strings.ReplaceAll(res, ".", "")
return
}