buffer support finished
This commit is contained in:
parent
f8d55b230d
commit
cc99c892f2
@ -70,7 +70,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
log.Printf("call result go->ts XorData = %v", resXor)
|
log.Printf("call result go->ts XorData = %x", resXor)
|
||||||
|
|
||||||
if err := ipc.Wait(1 * time.Second); err != nil {
|
if err := ipc.Wait(1 * time.Second); err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func (self *TsIpcApi) Div(
|
|||||||
) (
|
) (
|
||||||
int, error,
|
int, error,
|
||||||
) {
|
) {
|
||||||
results, err := self.Ipc.Call("TsIpcApi.Div", a, b)
|
results, err := self.Ipc.Call("TsIpcApi.Div", self.Ipc.Serialize(a), self.Ipc.Serialize(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)
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ func (self *TsIpcApi) XorData(
|
|||||||
) (
|
) (
|
||||||
[]byte, error,
|
[]byte, error,
|
||||||
) {
|
) {
|
||||||
results, err := self.Ipc.Call("TsIpcApi.XorData", data1, data2)
|
results, err := self.Ipc.Call("TsIpcApi.XorData", self.Ipc.Serialize(data1), self.Ipc.Serialize(data2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, fmt.Errorf("call to TsIpcApi.XorData failed: %w", err)
|
return []byte{}, fmt.Errorf("call to TsIpcApi.XorData failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,13 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
import "efprojects.com/kitten-ipc/types"
|
||||||
|
|
||||||
// todo check TInt size < 64
|
// todo check TInt size < 64
|
||||||
// todo check not float
|
// todo check not float
|
||||||
|
|
||||||
type ValType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
TInt ValType = 1
|
|
||||||
TString ValType = 2
|
|
||||||
TBool ValType = 3
|
|
||||||
TBlob ValType = 4
|
|
||||||
TArray ValType = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
func (v ValType) String() string {
|
|
||||||
switch v {
|
|
||||||
case TInt:
|
|
||||||
return "int"
|
|
||||||
case TString:
|
|
||||||
return "string"
|
|
||||||
case TBool:
|
|
||||||
return "bool"
|
|
||||||
case TBlob:
|
|
||||||
return "blob"
|
|
||||||
case TArray:
|
|
||||||
return "array"
|
|
||||||
default:
|
|
||||||
panic("unreachable code")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Val struct {
|
type Val struct {
|
||||||
Name string
|
Name string
|
||||||
Type ValType
|
Type types.ValType
|
||||||
Children []Val
|
Children []Val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
||||||
|
"efprojects.com/kitten-ipc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// todo: check int overflow
|
// todo: check int overflow
|
||||||
@ -40,24 +41,24 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
|
|||||||
"receiver": func(name string) string {
|
"receiver": func(name string) string {
|
||||||
return defaultReceiver
|
return defaultReceiver
|
||||||
},
|
},
|
||||||
"typedef": func(t api.ValType) (string, error) {
|
"typedef": func(t types.ValType) (string, error) {
|
||||||
td, ok := map[api.ValType]string{
|
td, ok := map[types.ValType]string{
|
||||||
api.TInt: "int",
|
types.TInt: "int",
|
||||||
api.TString: "string",
|
types.TString: "string",
|
||||||
api.TBool: "bool",
|
types.TBool: "bool",
|
||||||
api.TBlob: "[]byte",
|
types.TBlob: "[]byte",
|
||||||
}[t]
|
}[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("cannot generate type %v", t)
|
return "", fmt.Errorf("cannot generate type %v", t)
|
||||||
}
|
}
|
||||||
return td, nil
|
return td, nil
|
||||||
},
|
},
|
||||||
"convtype": func(valDef string, t api.ValType) (string, error) {
|
"convtype": func(valDef string, t types.ValType) (string, error) {
|
||||||
td, ok := map[api.ValType]string{
|
td, ok := map[types.ValType]string{
|
||||||
api.TInt: fmt.Sprintf("int(%s.(float64))", valDef),
|
types.TInt: fmt.Sprintf("int(%s.(float64))", valDef),
|
||||||
api.TString: fmt.Sprintf("%s.(string)", valDef),
|
types.TString: fmt.Sprintf("%s.(string)", valDef),
|
||||||
api.TBool: fmt.Sprintf("%s.(bool)", valDef),
|
types.TBool: fmt.Sprintf("%s.(bool)", valDef),
|
||||||
api.TBlob: fmt.Sprintf(
|
types.TBlob: fmt.Sprintf(
|
||||||
"%s.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(\"\"), %s).([]byte)",
|
"%s.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(\"\"), %s).([]byte)",
|
||||||
defaultReceiver,
|
defaultReceiver,
|
||||||
valDef,
|
valDef,
|
||||||
@ -68,12 +69,12 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
|
|||||||
}
|
}
|
||||||
return td, nil
|
return td, nil
|
||||||
},
|
},
|
||||||
"zerovalue": func(t api.ValType) (string, error) {
|
"zerovalue": func(t types.ValType) (string, error) {
|
||||||
v, ok := map[api.ValType]string{
|
v, ok := map[types.ValType]string{
|
||||||
api.TInt: "0",
|
types.TInt: "0",
|
||||||
api.TString: `""`,
|
types.TString: `""`,
|
||||||
api.TBool: "false",
|
types.TBool: "false",
|
||||||
api.TBlob: "[]byte{}",
|
types.TBlob: "[]byte{}",
|
||||||
}[t]
|
}[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("cannot generate zero value for type %v", t)
|
return "", fmt.Errorf("cannot generate zero value for type %v", t)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ package {{ .PkgName }}
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
kittenipc "efprojects.com/kitten-ipc"
|
kittenipc "efprojects.com/kitten-ipc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ func ({{ $e.Name | receiver }} *{{ $e.Name }}) {{ $mtd.Name }}(
|
|||||||
) (
|
) (
|
||||||
{{ range $mtd.Ret }}{{ .Type | typedef }}, {{ end }}error,
|
{{ range $mtd.Ret }}{{ .Type | typedef }}, {{ end }}error,
|
||||||
) {
|
) {
|
||||||
results, err := {{ $e.Name | receiver }}.Ipc.Call("{{ $e.Name }}.{{ $mtd.Name }}"{{ range $mtd.Params }}, {{ .Name }}{{ end }})
|
results, err := {{ $e.Name | receiver }}.Ipc.Call("{{ $e.Name }}.{{ $mtd.Name }}"{{ range $mtd.Params }}, self.Ipc.Serialize({{ .Name }}){{ end }})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return {{ range $mtd.Ret }}{{ .Type | zerovalue }}, {{ end }} fmt.Errorf("call to {{ $e.Name }}.{{ $mtd.Name }} failed: %w", err)
|
return {{ range $mtd.Ret }}{{ .Type | zerovalue }}, {{ end }} fmt.Errorf("call to {{ $e.Name }}.{{ $mtd.Name }} failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/common"
|
"efprojects.com/kitten-ipc/kitcom/internal/common"
|
||||||
|
"efprojects.com/kitten-ipc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`)
|
var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`)
|
||||||
@ -141,11 +142,11 @@ func fieldToVal(param *ast.Field, returning bool) (*api.Val, error) {
|
|||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
switch paramType.Name {
|
switch paramType.Name {
|
||||||
case "int":
|
case "int":
|
||||||
val.Type = api.TInt
|
val.Type = types.TInt
|
||||||
case "string":
|
case "string":
|
||||||
val.Type = api.TString
|
val.Type = types.TString
|
||||||
case "bool":
|
case "bool":
|
||||||
val.Type = api.TBool
|
val.Type = types.TBool
|
||||||
case "error":
|
case "error":
|
||||||
if returning {
|
if returning {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -160,7 +161,7 @@ func fieldToVal(param *ast.Field, returning bool) (*api.Val, error) {
|
|||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
switch elementType.Name {
|
switch elementType.Name {
|
||||||
case "byte":
|
case "byte":
|
||||||
val.Type = api.TBlob
|
val.Type = types.TBlob
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("parameter type %s is not supported yet", elementType.Name)
|
return nil, fmt.Errorf("parameter type %s is not supported yet", elementType.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
||||||
|
"efprojects.com/kitten-ipc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed tsgen.tmpl
|
//go:embed tsgen.tmpl
|
||||||
@ -30,12 +31,12 @@ func (g *TypescriptApiGenerator) Generate(apis *api.Api, destFile string) error
|
|||||||
|
|
||||||
tpl := template.New("tsgen")
|
tpl := template.New("tsgen")
|
||||||
tpl = tpl.Funcs(map[string]any{
|
tpl = tpl.Funcs(map[string]any{
|
||||||
"typedef": func(t api.ValType) (string, error) {
|
"typedef": func(t types.ValType) (string, error) {
|
||||||
td, ok := map[api.ValType]string{
|
td, ok := map[types.ValType]string{
|
||||||
api.TInt: "number",
|
types.TInt: "number",
|
||||||
api.TString: "string",
|
types.TString: "string",
|
||||||
api.TBool: "boolean",
|
types.TBool: "boolean",
|
||||||
api.TBlob: "Buffer",
|
types.TBlob: "Buffer",
|
||||||
}[t]
|
}[t]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("cannot generate type %v", t)
|
return "", fmt.Errorf("cannot generate type %v", t)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
|
||||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
||||||
|
"efprojects.com/kitten-ipc/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TypescriptApiParser struct {
|
type TypescriptApiParser struct {
|
||||||
@ -108,20 +109,20 @@ func (p *TypescriptApiParser) parseFile(sourceFilePath string) ([]api.Endpoint,
|
|||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TypescriptApiParser) fieldToVal(typ *ast.TypeNode) (api.ValType, error) {
|
func (p *TypescriptApiParser) fieldToVal(typ *ast.TypeNode) (types.ValType, error) {
|
||||||
switch typ.Kind {
|
switch typ.Kind {
|
||||||
case ast.KindNumberKeyword:
|
case ast.KindNumberKeyword:
|
||||||
return api.TInt, nil
|
return types.TInt, nil
|
||||||
case ast.KindStringKeyword:
|
case ast.KindStringKeyword:
|
||||||
return api.TString, nil
|
return types.TString, nil
|
||||||
case ast.KindBooleanKeyword:
|
case ast.KindBooleanKeyword:
|
||||||
return api.TBool, nil
|
return types.TBool, nil
|
||||||
case ast.KindTypeReference:
|
case ast.KindTypeReference:
|
||||||
refNode := typ.AsTypeReferenceNode()
|
refNode := typ.AsTypeReferenceNode()
|
||||||
ident := refNode.TypeName.AsIdentifier()
|
ident := refNode.TypeName.AsIdentifier()
|
||||||
switch ident.Text {
|
switch ident.Text {
|
||||||
case "Buffer":
|
case "Buffer":
|
||||||
return api.TBlob, nil
|
return types.TBlob, nil
|
||||||
default:
|
default:
|
||||||
return 0, fmt.Errorf("reference type %s is not supported yet", ident.Text)
|
return 0, fmt.Errorf("reference type %s is not supported yet", ident.Text)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"efprojects.com/kitten-ipc/types"
|
||||||
"github.com/samber/mo"
|
"github.com/samber/mo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ type Message struct {
|
|||||||
type IpcCommon interface {
|
type IpcCommon interface {
|
||||||
Call(method string, params ...any) (Vals, error)
|
Call(method string, params ...any) (Vals, error)
|
||||||
ConvType(needType reflect.Type, gotType reflect.Type, arg any) any
|
ConvType(needType reflect.Type, gotType reflect.Type, arg any) any
|
||||||
|
Serialize(arg any) any
|
||||||
}
|
}
|
||||||
|
|
||||||
type pendingCall struct {
|
type pendingCall struct {
|
||||||
@ -160,31 +162,6 @@ func (ipc *ipcCommon) handleCall(msg Message) {
|
|||||||
ipc.sendResponse(msg.Id, results, resErr)
|
ipc.sendResponse(msg.Id, results, resErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ipc *ipcCommon) ConvType(needType reflect.Type, gotType reflect.Type, arg any) any {
|
|
||||||
switch needType.Kind() {
|
|
||||||
case reflect.Int:
|
|
||||||
// JSON decodes any number to float64. If we need int, we should check and convert
|
|
||||||
if gotType.Kind() == reflect.Float64 {
|
|
||||||
floatArg := arg.(float64)
|
|
||||||
if float64(int64(floatArg)) == floatArg && !needType.OverflowInt(int64(floatArg)) {
|
|
||||||
arg = int(floatArg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
switch needType.Elem().Kind() {
|
|
||||||
case reflect.Uint8:
|
|
||||||
if gotType.Kind() == reflect.String {
|
|
||||||
var err error
|
|
||||||
arg, err = base64.StdEncoding.DecodeString(arg.(string))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("decode base64: %s", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ipc *ipcCommon) findMethod(methodName string) (reflect.Value, error) {
|
func (ipc *ipcCommon) findMethod(methodName string) (reflect.Value, error) {
|
||||||
parts := strings.Split(methodName, ".")
|
parts := strings.Split(methodName, ".")
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
@ -293,6 +270,46 @@ func (ipc *ipcCommon) closeConn() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ipc *ipcCommon) ConvType(needType reflect.Type, gotType reflect.Type, arg any) any {
|
||||||
|
switch needType.Kind() {
|
||||||
|
case reflect.Int:
|
||||||
|
// JSON decodes any number to float64. If we need int, we should check and convert
|
||||||
|
if gotType.Kind() == reflect.Float64 {
|
||||||
|
floatArg := arg.(float64)
|
||||||
|
if float64(int64(floatArg)) == floatArg && !needType.OverflowInt(int64(floatArg)) {
|
||||||
|
arg = int(floatArg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
switch needType.Elem().Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
if gotType.Kind() == reflect.String {
|
||||||
|
var err error
|
||||||
|
arg, err = base64.StdEncoding.DecodeString(arg.(string))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("decode base64: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ipc *ipcCommon) Serialize(arg any) any {
|
||||||
|
t := reflect.TypeOf(arg)
|
||||||
|
switch t.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
switch t.Elem().Name() {
|
||||||
|
case "uint8":
|
||||||
|
return map[string]any{
|
||||||
|
"t": types.TBlob.String(),
|
||||||
|
"d": base64.StdEncoding.EncodeToString(arg.([]byte)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
type ParentIPC struct {
|
type ParentIPC struct {
|
||||||
*ipcCommon
|
*ipcCommon
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
|
|||||||
28
lib/golang/types/types.go
Normal file
28
lib/golang/types/types.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
type ValType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TInt ValType = 1
|
||||||
|
TString ValType = 2
|
||||||
|
TBool ValType = 3
|
||||||
|
TBlob ValType = 4
|
||||||
|
TArray ValType = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v ValType) String() string {
|
||||||
|
switch v {
|
||||||
|
case TInt:
|
||||||
|
return "int"
|
||||||
|
case TString:
|
||||||
|
return "string"
|
||||||
|
case TBool:
|
||||||
|
return "bool"
|
||||||
|
case TBlob:
|
||||||
|
return "blob"
|
||||||
|
case TArray:
|
||||||
|
return "array"
|
||||||
|
default:
|
||||||
|
panic("unreachable code")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -142,7 +142,7 @@ abstract class IPCCommon {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.processingCalls++;
|
this.processingCalls++;
|
||||||
let result = method.apply(endpoint, msg.args);
|
let result = method.apply(endpoint, msg.args.map(arg => this.convType(arg)));
|
||||||
if (result instanceof Promise) {
|
if (result instanceof Promise) {
|
||||||
result = await result;
|
result = await result;
|
||||||
}
|
}
|
||||||
@ -203,11 +203,23 @@ abstract class IPCCommon {
|
|||||||
case 'boolean':
|
case 'boolean':
|
||||||
case 'number':
|
case 'number':
|
||||||
return arg;
|
return arg;
|
||||||
// @ts-expect-error TS7029
|
|
||||||
case 'object':
|
case 'object':
|
||||||
if(arg instanceof Buffer) {
|
if(arg instanceof Buffer) {
|
||||||
return arg.toString('base64');
|
return arg.toString('base64');
|
||||||
}
|
}
|
||||||
|
const keys = Object.entries(arg).map(p => p[0]).sort();
|
||||||
|
if(keys[0] === 'd' && keys[1] === 't') {
|
||||||
|
const type = arg['t'];
|
||||||
|
const data = arg['d'];
|
||||||
|
switch (type) {
|
||||||
|
case 'blob':
|
||||||
|
return Buffer.from(data, 'base64');
|
||||||
|
default:
|
||||||
|
throw new Error(`custom object type ${type} is not supported`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`got unknown arg type: object with keys ${keys}`);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`arg type ${typeof arg} is not supported`);
|
throw new Error(`arg type ${typeof arg} is not supported`);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user