From 12e472c8e57a75cc569ec772e2da774df23141e8 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Tue, 21 Oct 2025 18:46:52 +0300 Subject: [PATCH] tons of code --- example/golang/tsapi.gen.go | 17 +++++++++ example/ts/index.ts | 4 +- kitcom/go_gen.tmpl | 28 ++++++++++++++ kitcom/gogen.go | 74 +++++++++++++++++++++++++------------ kitcom/main.go | 51 ++++++++++++++----------- kitcom/tsparser.go | 24 +++++------- 6 files changed, 135 insertions(+), 63 deletions(-) create mode 100644 example/golang/tsapi.gen.go create mode 100644 kitcom/go_gen.tmpl diff --git a/example/golang/tsapi.gen.go b/example/golang/tsapi.gen.go new file mode 100644 index 0000000..78a00a7 --- /dev/null +++ b/example/golang/tsapi.gen.go @@ -0,0 +1,17 @@ +// Code generated by kitcom. DO NOT EDIT. + +package main + +import kittenipc "efprojects.com/kitten-ipc" + +type TsIpcApi struct { + Ipc *kittenipc.KittenIPC +} + +func (t *TsIpcApi) Div( + a int, b int, +) int { + return t.Ipc.Call( + "Div", a, b, + ) +} diff --git a/example/ts/index.ts b/example/ts/index.ts index 34f86ba..7e30706 100644 --- a/example/ts/index.ts +++ b/example/ts/index.ts @@ -1,7 +1,7 @@ /** * @kittenipc api */ -class IpcApi { +class TsIpcApi { Div(a: number, b: number): number { return a / b; } @@ -9,7 +9,7 @@ class IpcApi { function main() { - + } main() diff --git a/kitcom/go_gen.tmpl b/kitcom/go_gen.tmpl new file mode 100644 index 0000000..0a8a859 --- /dev/null +++ b/kitcom/go_gen.tmpl @@ -0,0 +1,28 @@ + +{{- /*gotype: efprojects.com/kitten-ipc/kitcom.genData*/ -}} + +// Code generated by kitcom. DO NOT EDIT. + +package {{.PkgName}} + +import kittenipc "efprojects.com/kitten-ipc" + +{{range $e := .Api.Endpoints}} + +type {{.Name}} struct { + Ipc *kittenipc.KittenIPC +} + +{{range $mtd := .Methods}} +func ({{$e.Name | receiver}} *{{$e.Name}}) {{$mtd.Name}} ( +{{range $mtd.Params}}{{.Name}} {{.Type | typedef}}, {{end}} +) ( +{{range $mtd.Ret}}{{.Type | typedef}}, {{end}} +) { + return {{$e.Name | receiver}}.Ipc.Call( + "{{$mtd.Name}}", {{range $mtd.Pars}}{{.Name}}, {{end}} + ) +} +{{end}} + +{{end}} diff --git a/kitcom/gogen.go b/kitcom/gogen.go index 84a5b9e..afbf656 100644 --- a/kitcom/gogen.go +++ b/kitcom/gogen.go @@ -1,49 +1,75 @@ package main import ( - "errors" + "bytes" "fmt" - "html/template" + "go/format" "os" - "path/filepath" "strings" + "text/template" ) -type GoApiGenerator struct { +type genData struct { + PkgName string + Api *Api } -var tpl = template.Must(template.New("gotpl").Parse(strings.TrimSpace(` - - - -`))) +type GoApiGenerator struct { + pkgName string +} func (g *GoApiGenerator) Generate(api *Api, destFile string) error { - destFileBak := filepath.Join(destFile, ".bak") - _, err := os.Stat(destFile) - if err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("stat destination file: %w", err) - } - if !errors.Is(err, os.ErrNotExist) { - if err := os.Rename(destFile, destFileBak); err != nil { - return fmt.Errorf("backup destination file: %w", err) - } + tplCtx := genData{ + PkgName: g.pkgName, + Api: api, } + tpl := template.New("gogen") + tpl = tpl.Funcs(map[string]any{ + "receiver": func(name string) string { + return strings.ToLower(name)[0:1] + }, + "typedef": func(t ValType) (string, error) { + td, ok := map[ValType]string{ + TInt: "int", + TString: "string", + TBool: "bool", + }[t] + if !ok { + return "", fmt.Errorf("cannot generate type %v", t) + } + return td, nil + }, + }) + tpl = template.Must(tpl.ParseFiles("./go_gen.tmpl")) + + var buf bytes.Buffer + + if err := tpl.ExecuteTemplate(&buf, "go_gen.tmpl", tplCtx); err != nil { + return fmt.Errorf("execute template: %w", err) + } + + if err := g.writeDest(destFile, buf.Bytes()); err != nil { + return fmt.Errorf("write file: %w", err) + } + + return nil +} + +func (g *GoApiGenerator) writeDest(destFile string, bytes []byte) error { f, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, 0644) if err != nil { return fmt.Errorf("open destination file: %w", err) } defer f.Close() - if err := tpl.Execute(f, api); err != nil { - return fmt.Errorf("execute template: %w", err) + formatted, err := format.Source(bytes) + if err != nil { + return fmt.Errorf("format source: %w", err) } - if _, err := os.Stat(destFileBak); err == nil { - if err := os.Remove(destFileBak); err != nil { - return fmt.Errorf("remove backup file: %w", err) - } + if _, err := f.Write(formatted); err != nil { + return fmt.Errorf("write formatted source: %w", err) } return nil } diff --git a/kitcom/main.go b/kitcom/main.go index 49e1d8e..29d5a88 100644 --- a/kitcom/main.go +++ b/kitcom/main.go @@ -3,23 +3,13 @@ package main import ( "flag" "fmt" + "go/token" "log" "os" "path" "path/filepath" ) -var ( - Src string - Dest string -) - -func parseFlags() { - flag.StringVar(&Src, "src", "", "Source file") - flag.StringVar(&Dest, "dest", "", "Dest file") - flag.Parse() -} - type ValType int // todo check TInt size < 64 @@ -40,13 +30,18 @@ type Val struct { } type Method struct { - Name string - Pars []Val - Ret []Val + Name string + Params []Val + Ret []Val +} + +type Endpoint struct { + Name string + Methods []Method } type Api struct { - Methods []Method + Endpoints []Endpoint } type ApiParser interface { @@ -64,17 +59,21 @@ func main() { // log.Panic("GOFILE must be set") //} - parseFlags() - if Src == "" || Dest == "" { + src := flag.String("src", "", "Source file") + dest := flag.String("dest", "", "Dest file") + pkgName := flag.String("pkgname", "", "Package name (for go)") + flag.Parse() + + if *src == "" || *dest == "" { log.Panic("source and destination must be set") } - srcAbs, err := filepath.Abs(Src) + srcAbs, err := filepath.Abs(*src) if err != nil { log.Panic(err) } - destAbs, err := filepath.Abs(Dest) + destAbs, err := filepath.Abs(*dest) if err != nil { log.Panic(err) } @@ -93,7 +92,7 @@ func main() { log.Panic(err) } - apiGenerator, err := apiGeneratorByExt(destAbs) + apiGenerator, err := apiGeneratorByExt(destAbs, *pkgName) if err != nil { log.Panic(err) } @@ -129,10 +128,18 @@ func apiParserByExt(src string) (ApiParser, error) { } } -func apiGeneratorByExt(dest string) (ApiGenerator, error) { +func apiGeneratorByExt(dest string, pkgName string) (ApiGenerator, error) { switch path.Ext(dest) { case ".go": - return &GoApiGenerator{}, nil + if pkgName == "" { + return nil, fmt.Errorf("package name must be set for Go generation") + } + if !token.IsIdentifier(pkgName) { + return nil, fmt.Errorf("invalid package name: %s", pkgName) + } + return &GoApiGenerator{ + pkgName: pkgName, + }, nil case ".ts": return &TypescriptApiGenerator{}, nil case ".js": diff --git a/kitcom/tsparser.go b/kitcom/tsparser.go index 3d03444..ba4db89 100644 --- a/kitcom/tsparser.go +++ b/kitcom/tsparser.go @@ -18,10 +18,6 @@ const TagComment = "api" type TypescriptApiParser struct { } -type apiClass struct { - methods []Method -} - func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { f, err := os.Open(sourceFilePath) @@ -44,7 +40,7 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { }, string(fileContents), core.ScriptKindTS) _ = sourceFile - var apiClasses []apiClass + var api Api sourceFile.ForEachChild(func(node *ast.Node) bool { if node.Kind != ast.KindClassDeclaration { @@ -78,7 +74,9 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { return false } - var apiCls apiClass + var endpoint Endpoint + + endpoint.Name = cls.Name().Text() for _, member := range cls.MemberList().Nodes { if member.Kind != ast.KindMethodDeclaration { @@ -103,7 +101,7 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { err = fmt.Errorf("parameter type %s is not supported yet", par.Type.Kind) return false } - apiMethod.Pars = append(apiMethod.Pars, apiPar) + apiMethod.Params = append(apiMethod.Params, apiPar) } var apiRet Val switch method.Type.Kind { @@ -118,10 +116,10 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { return false } apiMethod.Ret = []Val{apiRet} - apiCls.methods = append(apiCls.methods, apiMethod) + endpoint.Methods = append(endpoint.Methods, apiMethod) } - apiClasses = append(apiClasses, apiCls) + api.Endpoints = append(api.Endpoints, endpoint) return false }) @@ -130,13 +128,9 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*Api, error) { return nil, err } - if len(apiClasses) == 0 { + if len(api.Endpoints) == 0 { return nil, fmt.Errorf("no api class found") } - if len(apiClasses) > 1 { - return nil, fmt.Errorf("multiple api classes found") - } - - return &Api{Methods: apiClasses[0].methods}, nil + return &api, nil }