support dirs

This commit is contained in:
Egor Aristov 2025-10-27 17:29:08 +03:00
parent dfd0bf381f
commit 855d742203
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
5 changed files with 126 additions and 53 deletions

View File

@ -7,12 +7,8 @@ import (
"fmt" "fmt"
) )
type callable interface {
Call(method string, params ...any) (kittenipc.Vals, error)
}
type TsIpcApi struct { type TsIpcApi struct {
Ipc callable Ipc kittenipc.Callable
} }
func (t *TsIpcApi) Div( func (t *TsIpcApi) Div(
@ -20,9 +16,10 @@ func (t *TsIpcApi) Div(
) ( ) (
int, error, int, error,
) { ) {
results, err := t.Ipc.Call("TsIpcApi", "Div", a, b) results, err := t.Ipc.Call("TsIpcApi.Div", a, b)
if err != nil { if err != nil {
return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err) return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err)
} }
_ = results
return results[0].(int), nil return results[0].(int), nil
} }

View File

@ -1,13 +1,15 @@
// Code generated by kitcom. DO NOT EDIT.
import { ParentIPC, ChildIPC } from "kitten-ipc"; import { ParentIPC, ChildIPC } from "kitten-ipc";
export default class IpcApi { export default class IpcApi {
private ipc: ParentIPC | ChildIPC; protected ipc: ParentIPC | ChildIPC;
constructor(ipc: ParentIPC | ChildIPC) { constructor(ipc: ParentIPC | ChildIPC) {
this.ipc = ipc; this.ipc = ipc;
} }
async Div(a: number, b: number): Promise<number> { async Div(a: number, b: number): Promise<number> {
const results = await this.ipc.call("Div", a, b); const results = await this.ipc.call("IpcApi.Div", a, b);
return results[0] as number; return results[0] as number;
} }
} }

View File

@ -13,12 +13,35 @@ import (
var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`) var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`)
type GoApiParser struct { 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 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() fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, sourceFile, nil, parser.ParseComments|parser.SkipObjectResolution) astFile, err := parser.ParseFile(fileSet, sourceFile, nil, parser.ParseComments|parser.SkipObjectResolution)
if err != nil { if err != nil {
@ -52,13 +75,13 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) {
continue continue
} }
apis.Endpoints = append(apis.Endpoints, api.Endpoint{ endpoints = append(endpoints, api.Endpoint{
Name: typeSpec.Name.Name, Name: typeSpec.Name.Name,
}) })
} }
if len(apis.Endpoints) == 0 { if len(endpoints) == 0 {
return nil, fmt.Errorf("no api struct found") return nil, nil
} }
for _, decl := range astFile.Decls { for _, decl := range astFile.Decls {
@ -88,7 +111,7 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) {
continue continue
} }
for i, endpoint := range apis.Endpoints { for i, endpoint := range endpoints {
if recvIdent.Name == endpoint.Name { if recvIdent.Name == endpoint.Name {
var apiMethod api.Method var apiMethod api.Method
apiMethod.Name = funcDecl.Name.Name apiMethod.Name = funcDecl.Name.Name
@ -96,8 +119,8 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) {
var apiPar api.Val var apiPar api.Val
ident := param.Type.(*ast.Ident) ident := param.Type.(*ast.Ident)
switch ident.Name { switch ident.Name {
//case "int": case "int":
// apiPar.Type = api.TInt apiPar.Type = api.TInt
case "string": case "string":
apiPar.Type = api.TString apiPar.Type = api.TString
case "bool": case "bool":
@ -132,10 +155,9 @@ func (g *GoApiParser) Parse(sourceFile string) (*api.Api, error) {
} }
apiMethod.Ret = append(apiMethod.Ret, apiRet) 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 endpoints, nil
return &apis, nil
} }

View File

@ -4,6 +4,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"go/token" "go/token"
"io/fs"
"log" "log"
"os" "os"
"path" "path"
@ -15,7 +16,8 @@ import (
) )
type ApiParser interface { type ApiParser interface {
Parse(sourceFile string) (*api.Api, error) AddFile(path string)
Parse() (*api.Api, error)
} }
type ApiGenerator interface { type ApiGenerator interface {
@ -23,13 +25,7 @@ type ApiGenerator interface {
} }
func main() { func main() {
// todo support go:generate src := flag.String("src", "", "Source file/dir")
//goFile := os.Getenv("GOFILE")
//if goFile == "" {
// log.Panic("GOFILE must be set")
//}
src := flag.String("src", "", "Source file")
dest := flag.String("dest", "", "Dest file") dest := flag.String("dest", "", "Dest file")
pkgName := flag.String("pkgname", "", "Package name (for go)") pkgName := flag.String("pkgname", "", "Package name (for go)")
flag.Parse() flag.Parse()
@ -48,42 +44,79 @@ func main() {
log.Panic(err) log.Panic(err)
} }
if err := checkIsFile(srcAbs); err != nil { apiParser, err := apiParserByPath(srcAbs)
log.Panic(err)
}
apiParser, err := apiParserByExt(srcAbs)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
api, err := apiParser.Parse(srcAbs) apis, err := apiParser.Parse()
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
apiGenerator, err := apiGeneratorByExt(destAbs, *pkgName) apiGenerator, err := apiGeneratorByPath(destAbs, *pkgName)
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
if err := apiGenerator.Generate(api, destAbs); err != nil { if err := apiGenerator.Generate(apis, destAbs); err != nil {
log.Panic(err) log.Panic(err)
} }
} }
func checkIsFile(src string) error { func apiParserByPath(src string) (ApiParser, error) {
info, err := os.Stat(src)
s, err := os.Stat(src)
if err != nil { 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) { switch path.Ext(src) {
case ".go": case ".go":
return &golang.GoApiParser{}, nil 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) { switch path.Ext(dest) {
case ".go": case ".go":
if pkgName == "" { if pkgName == "" {

View File

@ -17,9 +17,34 @@ const TagName = "kittenipc"
const TagComment = "api" const TagComment = "api"
type TypescriptApiParser struct { 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) f, err := os.Open(sourceFilePath)
if err != nil { if err != nil {
@ -41,8 +66,6 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) {
}, string(fileContents), core.ScriptKindTS) }, string(fileContents), core.ScriptKindTS)
_ = sourceFile _ = sourceFile
var apis api.Api
sourceFile.ForEachChild(func(node *ast.Node) bool { sourceFile.ForEachChild(func(node *ast.Node) bool {
if node.Kind != ast.KindClassDeclaration { if node.Kind != ast.KindClassDeclaration {
return false return false
@ -107,8 +130,8 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) {
if method.Type != nil { if method.Type != nil {
var apiRet api.Val var apiRet api.Val
switch method.Type.Kind { switch method.Type.Kind {
//case ast.KindNumberKeyword: case ast.KindNumberKeyword:
// apiRet.Type = api.TInt apiRet.Type = api.TInt
case ast.KindStringKeyword: case ast.KindStringKeyword:
apiRet.Type = api.TString apiRet.Type = api.TString
case ast.KindBooleanKeyword: case ast.KindBooleanKeyword:
@ -122,7 +145,7 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) {
endpoint.Methods = append(endpoint.Methods, apiMethod) endpoint.Methods = append(endpoint.Methods, apiMethod)
} }
apis.Endpoints = append(apis.Endpoints, endpoint) endpoints = append(endpoints, endpoint)
return false return false
}) })
@ -131,9 +154,5 @@ func (t *TypescriptApiParser) Parse(sourceFilePath string) (*api.Api, error) {
return nil, err return nil, err
} }
if len(apis.Endpoints) == 0 { return endpoints, nil
return nil, fmt.Errorf("no api class found")
}
return &apis, nil
} }