buffer support finished
This commit is contained in:
parent
f8d55b230d
commit
cc99c892f2
@ -70,7 +70,7 @@ func main() {
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Panic(err)
|
||||
|
||||
@ -17,7 +17,7 @@ func (self *TsIpcApi) Div(
|
||||
) (
|
||||
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 {
|
||||
return 0, fmt.Errorf("call to TsIpcApi.Div failed: %w", err)
|
||||
}
|
||||
@ -30,7 +30,7 @@ func (self *TsIpcApi) XorData(
|
||||
) (
|
||||
[]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 {
|
||||
return []byte{}, fmt.Errorf("call to TsIpcApi.XorData failed: %w", err)
|
||||
}
|
||||
|
||||
@ -1,38 +1,13 @@
|
||||
package api
|
||||
|
||||
import "efprojects.com/kitten-ipc/types"
|
||||
|
||||
// todo check TInt size < 64
|
||||
// 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 {
|
||||
Name string
|
||||
Type ValType
|
||||
Type types.ValType
|
||||
Children []Val
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
_ "embed"
|
||||
|
||||
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
||||
"efprojects.com/kitten-ipc/types"
|
||||
)
|
||||
|
||||
// todo: check int overflow
|
||||
@ -40,24 +41,24 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
|
||||
"receiver": func(name string) string {
|
||||
return defaultReceiver
|
||||
},
|
||||
"typedef": func(t api.ValType) (string, error) {
|
||||
td, ok := map[api.ValType]string{
|
||||
api.TInt: "int",
|
||||
api.TString: "string",
|
||||
api.TBool: "bool",
|
||||
api.TBlob: "[]byte",
|
||||
"typedef": func(t types.ValType) (string, error) {
|
||||
td, ok := map[types.ValType]string{
|
||||
types.TInt: "int",
|
||||
types.TString: "string",
|
||||
types.TBool: "bool",
|
||||
types.TBlob: "[]byte",
|
||||
}[t]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot generate type %v", t)
|
||||
}
|
||||
return td, nil
|
||||
},
|
||||
"convtype": func(valDef string, t api.ValType) (string, error) {
|
||||
td, ok := map[api.ValType]string{
|
||||
api.TInt: fmt.Sprintf("int(%s.(float64))", valDef),
|
||||
api.TString: fmt.Sprintf("%s.(string)", valDef),
|
||||
api.TBool: fmt.Sprintf("%s.(bool)", valDef),
|
||||
api.TBlob: fmt.Sprintf(
|
||||
"convtype": func(valDef string, t types.ValType) (string, error) {
|
||||
td, ok := map[types.ValType]string{
|
||||
types.TInt: fmt.Sprintf("int(%s.(float64))", valDef),
|
||||
types.TString: fmt.Sprintf("%s.(string)", valDef),
|
||||
types.TBool: fmt.Sprintf("%s.(bool)", valDef),
|
||||
types.TBlob: fmt.Sprintf(
|
||||
"%s.Ipc.ConvType(reflect.TypeOf([]byte{}), reflect.TypeOf(\"\"), %s).([]byte)",
|
||||
defaultReceiver,
|
||||
valDef,
|
||||
@ -68,12 +69,12 @@ func (g *GoApiGenerator) Generate(apis *api.Api, destFile string) error {
|
||||
}
|
||||
return td, nil
|
||||
},
|
||||
"zerovalue": func(t api.ValType) (string, error) {
|
||||
v, ok := map[api.ValType]string{
|
||||
api.TInt: "0",
|
||||
api.TString: `""`,
|
||||
api.TBool: "false",
|
||||
api.TBlob: "[]byte{}",
|
||||
"zerovalue": func(t types.ValType) (string, error) {
|
||||
v, ok := map[types.ValType]string{
|
||||
types.TInt: "0",
|
||||
types.TString: `""`,
|
||||
types.TBool: "false",
|
||||
types.TBlob: "[]byte{}",
|
||||
}[t]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("cannot generate zero value for type %v", t)
|
||||
|
||||
@ -6,6 +6,7 @@ package {{ .PkgName }}
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
kittenipc "efprojects.com/kitten-ipc"
|
||||
)
|
||||
|
||||
@ -21,7 +22,7 @@ func ({{ $e.Name | receiver }} *{{ $e.Name }}) {{ $mtd.Name }}(
|
||||
) (
|
||||
{{ 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 {
|
||||
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/common"
|
||||
"efprojects.com/kitten-ipc/types"
|
||||
)
|
||||
|
||||
var decorComment = regexp.MustCompile(`^//\s?kittenipc:api$`)
|
||||
@ -141,11 +142,11 @@ func fieldToVal(param *ast.Field, returning bool) (*api.Val, error) {
|
||||
case *ast.Ident:
|
||||
switch paramType.Name {
|
||||
case "int":
|
||||
val.Type = api.TInt
|
||||
val.Type = types.TInt
|
||||
case "string":
|
||||
val.Type = api.TString
|
||||
val.Type = types.TString
|
||||
case "bool":
|
||||
val.Type = api.TBool
|
||||
val.Type = types.TBool
|
||||
case "error":
|
||||
if returning {
|
||||
return nil, nil
|
||||
@ -160,7 +161,7 @@ func fieldToVal(param *ast.Field, returning bool) (*api.Val, error) {
|
||||
case *ast.Ident:
|
||||
switch elementType.Name {
|
||||
case "byte":
|
||||
val.Type = api.TBlob
|
||||
val.Type = types.TBlob
|
||||
default:
|
||||
return nil, fmt.Errorf("parameter type %s is not supported yet", elementType.Name)
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
_ "embed"
|
||||
|
||||
"efprojects.com/kitten-ipc/kitcom/internal/api"
|
||||
"efprojects.com/kitten-ipc/types"
|
||||
)
|
||||
|
||||
//go:embed tsgen.tmpl
|
||||
@ -30,12 +31,12 @@ func (g *TypescriptApiGenerator) Generate(apis *api.Api, destFile string) error
|
||||
|
||||
tpl := template.New("tsgen")
|
||||
tpl = tpl.Funcs(map[string]any{
|
||||
"typedef": func(t api.ValType) (string, error) {
|
||||
td, ok := map[api.ValType]string{
|
||||
api.TInt: "number",
|
||||
api.TString: "string",
|
||||
api.TBool: "boolean",
|
||||
api.TBlob: "Buffer",
|
||||
"typedef": func(t types.ValType) (string, error) {
|
||||
td, ok := map[types.ValType]string{
|
||||
types.TInt: "number",
|
||||
types.TString: "string",
|
||||
types.TBool: "boolean",
|
||||
types.TBlob: "Buffer",
|
||||
}[t]
|
||||
if !ok {
|
||||
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/parser"
|
||||
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
||||
"efprojects.com/kitten-ipc/types"
|
||||
)
|
||||
|
||||
type TypescriptApiParser struct {
|
||||
@ -108,20 +109,20 @@ func (p *TypescriptApiParser) parseFile(sourceFilePath string) ([]api.Endpoint,
|
||||
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 {
|
||||
case ast.KindNumberKeyword:
|
||||
return api.TInt, nil
|
||||
return types.TInt, nil
|
||||
case ast.KindStringKeyword:
|
||||
return api.TString, nil
|
||||
return types.TString, nil
|
||||
case ast.KindBooleanKeyword:
|
||||
return api.TBool, nil
|
||||
return types.TBool, nil
|
||||
case ast.KindTypeReference:
|
||||
refNode := typ.AsTypeReferenceNode()
|
||||
ident := refNode.TypeName.AsIdentifier()
|
||||
switch ident.Text {
|
||||
case "Buffer":
|
||||
return api.TBlob, nil
|
||||
return types.TBlob, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("reference type %s is not supported yet", ident.Text)
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"efprojects.com/kitten-ipc/types"
|
||||
"github.com/samber/mo"
|
||||
)
|
||||
|
||||
@ -48,6 +49,7 @@ type Message struct {
|
||||
type IpcCommon interface {
|
||||
Call(method string, params ...any) (Vals, error)
|
||||
ConvType(needType reflect.Type, gotType reflect.Type, arg any) any
|
||||
Serialize(arg any) any
|
||||
}
|
||||
|
||||
type pendingCall struct {
|
||||
@ -160,31 +162,6 @@ func (ipc *ipcCommon) handleCall(msg Message) {
|
||||
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) {
|
||||
parts := strings.Split(methodName, ".")
|
||||
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 {
|
||||
*ipcCommon
|
||||
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 {
|
||||
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) {
|
||||
result = await result;
|
||||
}
|
||||
@ -203,11 +203,23 @@ abstract class IPCCommon {
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
return arg;
|
||||
// @ts-expect-error TS7029
|
||||
case 'object':
|
||||
if(arg instanceof Buffer) {
|
||||
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:
|
||||
throw new Error(`arg type ${typeof arg} is not supported`);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user