From 855d742203c888de2ac061d3731aae5eb0e846e4 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Mon, 27 Oct 2025 17:29:08 +0300 Subject: [PATCH] support dirs --- example/golang/tsapi.gen.go | 9 ++--- example/ts/src/goapi.gen.ts | 6 ++- kitcom/golang/goparser.go | 42 ++++++++++++++----- kitcom/main.go | 81 ++++++++++++++++++++++++++----------- kitcom/ts/tsparser.go | 41 ++++++++++++++----- 5 files changed, 126 insertions(+), 53 deletions(-) diff --git a/example/golang/tsapi.gen.go b/example/golang/tsapi.gen.go index 9150f49..2599c79 100644 --- a/example/golang/tsapi.gen.go +++ b/example/golang/tsapi.gen.go @@ -7,12 +7,8 @@ import ( "fmt" ) -type callable interface { - Call(method string, params ...any) (kittenipc.Vals, error) -} - type TsIpcApi struct { - Ipc callable + Ipc kittenipc.Callable } func (t *TsIpcApi) Div( @@ -20,9 +16,10 @@ func (t *TsIpcApi) Div( ) ( int, error, ) { - results, err := t.Ipc.Call("TsIpcApi", "Div", a, b) + results, err := t.Ipc.Call("TsIpcApi.Div", a, b) if err != nil { return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err) } + _ = results return results[0].(int), nil } diff --git a/example/ts/src/goapi.gen.ts b/example/ts/src/goapi.gen.ts index 23dafcf..e11a181 100644 --- a/example/ts/src/goapi.gen.ts +++ b/example/ts/src/goapi.gen.ts @@ -1,13 +1,15 @@ +// Code generated by kitcom. DO NOT EDIT. + import { ParentIPC, ChildIPC } from "kitten-ipc"; export default class IpcApi { - private ipc: ParentIPC | ChildIPC; + protected ipc: ParentIPC | ChildIPC; constructor(ipc: ParentIPC | ChildIPC) { this.ipc = ipc; } async Div(a: number, b: number): Promise { - const results = await this.ipc.call("Div", a, b); + const results = await this.ipc.call("IpcApi.Div", a, b); return results[0] as number; } } diff --git a/kitcom/golang/goparser.go b/kitcom/golang/goparser.go index 1fa9eef..4b8c73d 100644 --- a/kitcom/golang/goparser.go +++ b/kitcom/golang/goparser.go @@ -13,12 +13,35 @@ import ( var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`) type GoApiParser struct { + files []string } -func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) { +func (g *GoApiParser) AddFile(path string) { + g.files = append(g.files, path) +} + +func (g *GoApiParser) Parse() (*api.Api, error) { var apis api.Api + for _, f := range g.files { + endpoints, err := g.parseFile(f) + if err != nil { + return nil, fmt.Errorf("parse file: %w", err) + } + apis.Endpoints = append(apis.Endpoints, endpoints...) + } + + if len(apis.Endpoints) == 0 { + return nil, fmt.Errorf("no endpoints found") + } + + return &apis, nil +} + +func (g *GoApiParser) parseFile(sourceFile string) ([]api.Endpoint, error) { + var endpoints []api.Endpoint + fileSet := token.NewFileSet() astFile, err := parser.ParseFile(fileSet, sourceFile, nil, parser.ParseComments|parser.SkipObjectResolution) if err != nil { @@ -52,13 +75,13 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) { continue } - apis.Endpoints = append(apis.Endpoints, api.Endpoint{ + endpoints = append(endpoints, api.Endpoint{ Name: typeSpec.Name.Name, }) } - if len(apis.Endpoints) == 0 { - return nil, fmt.Errorf("no api struct found") + if len(endpoints) == 0 { + return nil, nil } for _, decl := range astFile.Decls { @@ -88,7 +111,7 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) { continue } - for i, endpoint := range apis.Endpoints { + for i, endpoint := range endpoints { if recvIdent.Name == endpoint.Name { var apiMethod api.Method apiMethod.Name = funcDecl.Name.Name @@ -96,8 +119,8 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) { var apiPar api.Val ident := param.Type.(*ast.Ident) switch ident.Name { - //case "int": - // apiPar.Type = api.TInt + case "int": + apiPar.Type = api.TInt case "string": apiPar.Type = api.TString case "bool": @@ -132,10 +155,9 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) { } apiMethod.Ret = append(apiMethod.Ret, apiRet) } - apis.Endpoints[i].Methods = append(apis.Endpoints[i].Methods, apiMethod) + endpoints[i].Methods = append(endpoints[i].Methods, apiMethod) } } } - - return &apis, nil + return endpoints, nil } diff --git a/kitcom/main.go b/kitcom/main.go index 2b90e29..258fa4a 100644 --- a/kitcom/main.go +++ b/kitcom/main.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "go/token" + "io/fs" "log" "os" "path" @@ -15,7 +16,8 @@ import ( ) type ApiParser interface { - Parse(sourceFile string) (*api.Api, error) + AddFile(path string) + Parse() (*api.Api, error) } type ApiGenerator interface { @@ -23,13 +25,7 @@ type ApiGenerator interface { } func main() { - // todo support go:generate - //goFile := os.Getenv("GOFILE") - //if goFile == "" { - // log.Panic("GOFILE must be set") - //} - - src := flag.String("src", "", "Source file") + src := flag.String("src", "", "Source file/dir") dest := flag.String("dest", "", "Dest file") pkgName := flag.String("pkgname", "", "Package name (for go)") flag.Parse() @@ -48,42 +44,79 @@ func main() { log.Panic(err) } - if err := checkIsFile(srcAbs); err != nil { - log.Panic(err) - } - - apiParser, err := apiParserByExt(srcAbs) + apiParser, err := apiParserByPath(srcAbs) if err != nil { log.Panic(err) } - api, err := apiParser.Parse(srcAbs) + apis, err := apiParser.Parse() if err != nil { log.Panic(err) } - apiGenerator, err := apiGeneratorByExt(destAbs, *pkgName) + apiGenerator, err := apiGeneratorByPath(destAbs, *pkgName) if err != nil { log.Panic(err) } - if err := apiGenerator.Generate(api, destAbs); err != nil { + if err := apiGenerator.Generate(apis, destAbs); err != nil { log.Panic(err) } } -func checkIsFile(src string) error { - info, err := os.Stat(src) +func apiParserByPath(src string) (ApiParser, error) { + + s, err := os.Stat(src) if err != nil { - return fmt.Errorf("stat file: %w", err) + return nil, fmt.Errorf("stat src: %w", err) } - if info.IsDir() { - return fmt.Errorf("%s is a directory; directories are not supported yet", src) + + var parser ApiParser + var ext string + + if s.IsDir() { + if err := filepath.Walk(src, func(curPath string, i fs.FileInfo, err error) error { + if err != nil { + return err + } + if i.IsDir() { + return nil + } + + p, err := apiParserByFilePath(i.Name()) + if err == nil { + if parser != nil { + if path.Ext(i.Name()) == ext { + parser.AddFile(curPath) + return nil + } + return fmt.Errorf("path contain multiple supported filetypes") + } else { + ext = path.Ext(i.Name()) + parser = p + parser.AddFile(curPath) + } + } + + return nil + }); err != nil { + return nil, fmt.Errorf("walk dir: %w", err) + } + } else { + parser, err = apiParserByFilePath(src) + if err != nil { + return nil, err + } + parser.AddFile(src) } - return nil + + if parser == nil { + return nil, fmt.Errorf("could not find supported files in %s", src) + } + return parser, nil } -func apiParserByExt(src string) (ApiParser, error) { +func apiParserByFilePath(src string) (ApiParser, error) { switch path.Ext(src) { case ".go": return &golang.GoApiParser{}, nil @@ -98,7 +131,7 @@ func apiParserByExt(src string) (ApiParser, error) { } } -func apiGeneratorByExt(dest string, pkgName string) (ApiGenerator, error) { +func apiGeneratorByPath(dest string, pkgName string) (ApiGenerator, error) { switch path.Ext(dest) { case ".go": if pkgName == "" { diff --git a/kitcom/ts/tsparser.go b/kitcom/ts/tsparser.go index a3e2ee3..cb466ca 100644 --- a/kitcom/ts/tsparser.go +++ b/kitcom/ts/tsparser.go @@ -17,9 +17,34 @@ const TagName = "kittenipc" const TagComment = "api" type TypescriptApiParser struct { + files []string } -func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) { +func (t *TypescriptApiParser) AddFile(path string) { + t.files = append(t.files, path) +} + +func (t *TypescriptApiParser) Parse() (*api.Api, error) { + + var apis api.Api + + for _, f := range t.files { + endpoints, err := t.parseFile(f) + if err != nil { + return nil, fmt.Errorf("parse file: %w", err) + } + apis.Endpoints = append(apis.Endpoints, endpoints...) + } + + if len(apis.Endpoints) == 0 { + return nil, fmt.Errorf("no endpoints found") + } + + return &apis, nil +} + +func (t *TypescriptApiParser) parseFile(sourceFilePath string) ([]api.Endpoint, error) { + var endpoints []api.Endpoint f, err := os.Open(sourceFilePath) if err != nil { @@ -41,8 +66,6 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) { }, string(fileContents), core.ScriptKindTS) _ = sourceFile - var apis api.Api - sourceFile.ForEachChild(func(node *ast.Node) bool { if node.Kind != ast.KindClassDeclaration { return false @@ -107,8 +130,8 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) { if method.Type != nil { var apiRet api.Val switch method.Type.Kind { - //case ast.KindNumberKeyword: - // apiRet.Type = api.TInt + case ast.KindNumberKeyword: + apiRet.Type = api.TInt case ast.KindStringKeyword: apiRet.Type = api.TString case ast.KindBooleanKeyword: @@ -122,7 +145,7 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) { endpoint.Methods = append(endpoint.Methods, apiMethod) } - apis.Endpoints = append(apis.Endpoints, endpoint) + endpoints = append(endpoints, endpoint) return false }) @@ -131,9 +154,5 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) { return nil, err } - if len(apis.Endpoints) == 0 { - return nil, fmt.Errorf("no api class found") - } - - return &apis, nil + return endpoints, nil }