627 lines
16 KiB
Go
627 lines
16 KiB
Go
package js
|
|
|
|
import (
|
|
"encoding"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"sirherobrine23.com.br/Sirherobrine23/napi-go"
|
|
)
|
|
|
|
const propertiesTagName = "napi"
|
|
|
|
// Convert go types to valid NAPI, if not conpatible return Undefined.
|
|
func ValueOf(env napi.EnvType, value any) (napiValue napi.ValueType, err error) {
|
|
return valueOf(env, reflect.ValueOf(value))
|
|
}
|
|
|
|
// Convert NAPI value to Go values
|
|
func ValueFrom(napiValue napi.ValueType, v any) error {
|
|
ptr := reflect.ValueOf(v)
|
|
if ptr.Kind() != reflect.Pointer {
|
|
return fmt.Errorf("require point to convert napi value to go value")
|
|
}
|
|
return valueFrom(napiValue, ptr.Elem())
|
|
}
|
|
|
|
func valueOf(env napi.EnvType, ptr reflect.Value) (napiValue napi.ValueType, err error) {
|
|
defer func(err *error) {
|
|
if err2 := recover(); err2 != nil {
|
|
switch v := err2.(type) {
|
|
case error:
|
|
*err = v
|
|
default:
|
|
*err = fmt.Errorf("panic recover: %s", err2)
|
|
}
|
|
}
|
|
}(&err)
|
|
|
|
ptrType := ptr.Type()
|
|
if ptrType.ConvertibleTo(reflect.TypeFor[napi.ValueType]()) {
|
|
if ptr.IsValid() {
|
|
return ptr.Interface().(napi.ValueType), nil
|
|
}
|
|
return nil, nil
|
|
} else if !ptr.IsValid() {
|
|
return env.Undefined()
|
|
} else if !ptr.IsZero() && ptr.CanInterface() { // Marshalers
|
|
switch v := ptr.Interface().(type) {
|
|
case time.Time:
|
|
return napi.CreateDate(env, v)
|
|
case encoding.TextMarshaler:
|
|
data, err := v.MarshalText()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return napi.CreateString(env, string(data))
|
|
case json.Marshaler:
|
|
var pointData any
|
|
data, err := v.MarshalJSON()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if err = json.Unmarshal(data, &pointData); err != nil {
|
|
return nil, err
|
|
}
|
|
return ValueOf(env, pointData)
|
|
}
|
|
}
|
|
|
|
switch ptrType.Kind() {
|
|
case reflect.Pointer:
|
|
return valueOf(env, ptr.Elem())
|
|
case reflect.String:
|
|
return napi.CreateString(env, ptr.String())
|
|
case reflect.Bool:
|
|
return napi.CreateBoolean(env, ptr.Bool())
|
|
case reflect.Int, reflect.Uint, reflect.Int32, reflect.Uint32, reflect.Float32, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16:
|
|
return napi.CreateNumber(env, ptr.Int())
|
|
case reflect.Float64:
|
|
return napi.CreateNumber(env, ptr.Float())
|
|
case reflect.Int64, reflect.Uint64:
|
|
return napi.CreateBigint(env, ptr.Int())
|
|
case reflect.Func:
|
|
return funcOf(env, ptr)
|
|
case reflect.Slice, reflect.Array:
|
|
arr, err := napi.CreateArray(env, ptr.Len())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for index := range ptr.Len() {
|
|
value, err := valueOf(env, ptr.Index(index))
|
|
if err != nil {
|
|
return arr, err
|
|
} else if err = arr.Set(index, value); err != nil {
|
|
return arr, err
|
|
}
|
|
}
|
|
return arr, nil
|
|
case reflect.Struct:
|
|
obj, err := napi.CreateObject(env)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for keyIndex := range ptrType.NumField() {
|
|
field, fieldType := ptr.Field(keyIndex), ptrType.Field(keyIndex)
|
|
if !fieldType.IsExported() || fieldType.Tag.Get(propertiesTagName) == "-" {
|
|
continue
|
|
}
|
|
|
|
value, err := valueOf(env, field)
|
|
if err != nil {
|
|
return obj, err
|
|
}
|
|
|
|
typeof, err := value.Type()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
keyNamed := fieldType.Name
|
|
if strings.Count(fieldType.Tag.Get(propertiesTagName), ",") > 0 {
|
|
fields := strings.SplitN(fieldType.Tag.Get(propertiesTagName), ",", 2)
|
|
keyNamed = fields[0]
|
|
switch fields[1] {
|
|
case "omitempty":
|
|
switch typeof {
|
|
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
|
|
continue
|
|
case napi.TypeString:
|
|
str, err := napi.ToString(value).Utf8Value()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if str == "" {
|
|
continue
|
|
}
|
|
}
|
|
case "omitzero":
|
|
switch typeof {
|
|
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
|
|
continue
|
|
case napi.TypeDate:
|
|
value, err := napi.ToDate(value).Time()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if value.Unix() == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeBigInt:
|
|
value, err := napi.ToBigint(value).Int64()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if value == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeNumber:
|
|
value, err := napi.ToNumber(value).Int()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if value == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeArray:
|
|
value, err := napi.ToArray(value).Length()
|
|
if err != nil {
|
|
return nil, err
|
|
} else if value == 0 {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if err = obj.Set(keyNamed, value); err != nil {
|
|
return obj, err
|
|
}
|
|
}
|
|
|
|
return obj, nil
|
|
case reflect.Map:
|
|
obj, err := napi.CreateObject(env)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for ptrKey, ptrValue := range ptr.Seq2() {
|
|
key, err := valueOf(env, ptrKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
value, err := valueOf(env, ptrValue)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if err = obj.SetWithValue(key, value); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return obj, nil
|
|
case reflect.Interface:
|
|
if ptr.IsValid() {
|
|
if ptr.IsNil() {
|
|
return env.Null()
|
|
} else if ptr.CanInterface() {
|
|
return valueOf(env, reflect.ValueOf(ptr.Interface()))
|
|
}
|
|
}
|
|
}
|
|
return env.Undefined()
|
|
}
|
|
|
|
// Convert javascript value to go typed value
|
|
func valueFrom(jsValue napi.ValueType, ptr reflect.Value) error {
|
|
typeOf, err := jsValue.Type()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ptrType := ptr.Type()
|
|
if ptrType.ConvertibleTo(reflect.TypeFor[napi.ValueType]()) {
|
|
switch typeOf {
|
|
case napi.TypeUndefined:
|
|
und, err := jsValue.Env().Undefined()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(und))
|
|
case napi.TypeNull:
|
|
null, err := jsValue.Env().Null()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(null))
|
|
case napi.TypeBoolean:
|
|
ptr.Set(reflect.ValueOf(napi.ToBoolean(jsValue)))
|
|
case napi.TypeNumber:
|
|
ptr.Set(reflect.ValueOf(napi.ToNumber(jsValue)))
|
|
case napi.TypeBigInt:
|
|
ptr.Set(reflect.ValueOf(napi.ToBigint(jsValue)))
|
|
case napi.TypeString:
|
|
ptr.Set(reflect.ValueOf(napi.ToString(jsValue)))
|
|
case napi.TypeSymbol:
|
|
// ptr.Set(reflect.ValueOf(napi.ToString(jsValue)))
|
|
case napi.TypeObject:
|
|
ptr.Set(reflect.ValueOf(napi.ToObject(jsValue)))
|
|
case napi.TypeFunction:
|
|
ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
case napi.TypeExternal:
|
|
// ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
case napi.TypeTypedArray:
|
|
// ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
case napi.TypePromise:
|
|
// ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
case napi.TypeDataView:
|
|
// ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
case napi.TypeBuffer:
|
|
ptr.Set(reflect.ValueOf(napi.ToBuffer(jsValue)))
|
|
case napi.TypeDate:
|
|
ptr.Set(reflect.ValueOf(napi.ToDate(jsValue)))
|
|
case napi.TypeArray:
|
|
ptr.Set(reflect.ValueOf(napi.ToArray(jsValue)))
|
|
case napi.TypeArrayBuffer:
|
|
// ptr.Set(reflect.ValueOf(napi.ToArray(jsValue)))
|
|
case napi.TypeError:
|
|
ptr.Set(reflect.ValueOf(napi.ToError(jsValue)))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
switch ptrType.Kind() {
|
|
case reflect.Pointer:
|
|
return valueFrom(jsValue, ptr.Elem())
|
|
case reflect.Interface:
|
|
// Check if is any and can set
|
|
if ptr.CanSet() && ptrType == reflect.TypeFor[any]() {
|
|
switch typeOf {
|
|
case napi.TypeNull, napi.TypeUndefined, napi.TypeUnkown:
|
|
ptr.Set(reflect.Zero(ptrType))
|
|
return nil
|
|
case napi.TypeBoolean:
|
|
valueOf, err := napi.ToBoolean(jsValue).Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(valueOf))
|
|
case napi.TypeNumber:
|
|
numberValue, err := napi.ToNumber(jsValue).Float()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(numberValue))
|
|
case napi.TypeBigInt:
|
|
numberValue, err := napi.ToBigint(jsValue).Int64()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(numberValue))
|
|
case napi.TypeString:
|
|
str, err := napi.ToString(jsValue).Utf8Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(str))
|
|
case napi.TypeDate:
|
|
timeDate, err := napi.ToDate(jsValue).Time()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(timeDate))
|
|
case napi.TypeArray:
|
|
napiArray := napi.ToArray(jsValue)
|
|
size, err := napiArray.Length()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
value := reflect.MakeSlice(reflect.SliceOf(ptrType), size, size)
|
|
for index := range size {
|
|
napiValue, err := napiArray.Get(index)
|
|
if err != nil {
|
|
return err
|
|
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ptr.Set(value)
|
|
case napi.TypeBuffer:
|
|
buff, err := napi.ToBuffer(jsValue).Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(buff))
|
|
case napi.TypeObject:
|
|
obj := napi.ToObject(jsValue)
|
|
goMap := reflect.MakeMap(reflect.MapOf(reflect.TypeFor[string](), reflect.TypeFor[any]()))
|
|
for keyName, value := range obj.Seq() {
|
|
valueOf := reflect.New(reflect.TypeFor[any]())
|
|
if err := valueFrom(value, valueOf); err != nil {
|
|
return err
|
|
}
|
|
goMap.SetMapIndex(reflect.ValueOf(keyName), valueOf)
|
|
}
|
|
ptr.Set(goMap)
|
|
case napi.TypeFunction:
|
|
ptr.Set(reflect.ValueOf(napi.ToFunction(jsValue)))
|
|
}
|
|
return nil
|
|
}
|
|
return fmt.Errorf("cannot set value, returned %s", typeOf)
|
|
}
|
|
|
|
switch typeOf {
|
|
case napi.TypeNull, napi.TypeUndefined, napi.TypeUnkown:
|
|
switch ptrType.Kind() {
|
|
case reflect.Interface, reflect.Pointer:
|
|
ptr.Set(reflect.Zero(ptrType))
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot set value, returned %s", typeOf)
|
|
}
|
|
case napi.TypeBoolean:
|
|
switch ptr.Kind() {
|
|
case reflect.Bool:
|
|
valueOf, err := napi.ToBoolean(jsValue).Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetBool(valueOf)
|
|
default:
|
|
return fmt.Errorf("cannot set boolean value to %s", ptr.Kind())
|
|
}
|
|
case napi.TypeNumber:
|
|
switch ptrType.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
floatValue, err := napi.ToNumber(jsValue).Float()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetFloat(floatValue)
|
|
return nil
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
numberValue, err := napi.ToNumber(jsValue).Int()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetInt(numberValue)
|
|
return nil
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
numberValue, err := napi.ToNumber(jsValue).Int()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetUint(uint64(numberValue))
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot set number value to %s", ptr.Kind())
|
|
}
|
|
case napi.TypeBigInt:
|
|
switch ptrType.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
numberValue, err := napi.ToNumber(jsValue).Int()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetInt(numberValue)
|
|
return nil
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
numberValue, err := napi.ToNumber(jsValue).Int()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetUint(uint64(numberValue))
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot set number value to %s", ptr.Kind())
|
|
}
|
|
case napi.TypeString:
|
|
switch ptr.Kind() {
|
|
case reflect.String:
|
|
default:
|
|
return fmt.Errorf("cannot set string to %s", ptr.Kind())
|
|
}
|
|
str, err := napi.ToString(jsValue).Utf8Value()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(str))
|
|
return nil
|
|
case napi.TypeDate:
|
|
switch ptrType.Kind() {
|
|
case reflect.Struct:
|
|
if ptrType == reflect.TypeFor[time.Time]() {
|
|
break
|
|
}
|
|
fallthrough
|
|
default:
|
|
return fmt.Errorf("cannot set Date to %s", ptr.Kind())
|
|
}
|
|
timeDate, err := napi.ToDate(jsValue).Time()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.Set(reflect.ValueOf(timeDate))
|
|
return nil
|
|
case napi.TypeArray:
|
|
napiArray := napi.ToArray(jsValue)
|
|
size, err := napiArray.Length()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch ptr.Kind() {
|
|
case reflect.Slice:
|
|
value := reflect.MakeSlice(ptrType, size, size)
|
|
for index := range size {
|
|
napiValue, err := napiArray.Get(index)
|
|
if err != nil {
|
|
return err
|
|
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ptr.Set(value)
|
|
return nil
|
|
case reflect.Array:
|
|
value := reflect.New(ptrType)
|
|
for index := range min(size, value.Len()) {
|
|
napiValue, err := napiArray.Get(index)
|
|
if err != nil {
|
|
return err
|
|
} else if err = valueFrom(napiValue, value.Index(index)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
ptr.Set(value)
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot set Array to %s", ptr.Kind())
|
|
}
|
|
case napi.TypeBuffer:
|
|
switch ptr.Kind() {
|
|
case reflect.Slice:
|
|
if ptrType == reflect.TypeFor[[]byte]() {
|
|
break
|
|
}
|
|
fallthrough
|
|
default:
|
|
return fmt.Errorf("cannot set Buffer to %s", ptr.Kind())
|
|
}
|
|
buff, err := napi.ToBuffer(jsValue).Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ptr.SetBytes(buff)
|
|
return nil
|
|
case napi.TypeObject:
|
|
obj := napi.ToObject(jsValue)
|
|
switch ptr.Kind() {
|
|
case reflect.Struct:
|
|
ptr.Set(reflect.New(ptrType).Elem())
|
|
for keyIndex := range ptrType.NumField() {
|
|
field, fieldType := ptr.Field(keyIndex), ptrType.Field(keyIndex)
|
|
if !fieldType.IsExported() || fieldType.Tag.Get(propertiesTagName) == "-" {
|
|
continue
|
|
}
|
|
|
|
keyName, omitEmpty, omitZero := fieldType.Name, false, false
|
|
if strings.Count(fieldType.Tag.Get(propertiesTagName), ",") > 0 {
|
|
fields := strings.SplitN(fieldType.Tag.Get(propertiesTagName), ",", 2)
|
|
keyName = fields[0]
|
|
switch fields[1] {
|
|
case "omitempty":
|
|
omitEmpty = true
|
|
case "omitzero":
|
|
omitZero = true
|
|
}
|
|
} else {
|
|
omitEmpty, omitZero = true, true
|
|
}
|
|
|
|
if ok, err := obj.Has(keyName); err != nil {
|
|
return err
|
|
} else if !ok && !(omitEmpty || omitZero) {
|
|
return fmt.Errorf("cannot set %s to %s", keyName, ptr.Kind())
|
|
}
|
|
|
|
value, err := obj.Get(keyName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
valueTypeof, _ := value.Type()
|
|
if omitEmpty || omitZero {
|
|
switch valueTypeof {
|
|
case napi.TypeUndefined, napi.TypeNull, napi.TypeUnkown:
|
|
continue
|
|
case napi.TypeString:
|
|
if str, _ := napi.ToString(value).Utf8Value(); str == "" {
|
|
continue
|
|
}
|
|
case napi.TypeDate:
|
|
if timeDate, _ := napi.ToDate(value).Time(); timeDate.Unix() == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeBigInt:
|
|
if numberValue, _ := napi.ToBigint(value).Int64(); numberValue == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeNumber:
|
|
if numberValue, _ := napi.ToNumber(value).Int(); numberValue == 0 {
|
|
continue
|
|
}
|
|
case napi.TypeArray:
|
|
if size, _ := napi.ToArray(value).Length(); size == 0 {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
valueOf := reflect.New(fieldType.Type).Elem()
|
|
if err := valueFrom(value, valueOf); err != nil {
|
|
return err
|
|
}
|
|
field.Set(valueOf)
|
|
}
|
|
return nil
|
|
case reflect.Map:
|
|
// Check if key is string, bool, int*, uint*, float*, else return error
|
|
switch ptrType.Key().Kind() {
|
|
case reflect.String:
|
|
case reflect.Bool:
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
case reflect.Float32, reflect.Float64:
|
|
default:
|
|
return fmt.Errorf("cannot set Object to %s", ptr.Kind())
|
|
}
|
|
|
|
goMap := reflect.MakeMap(ptrType)
|
|
for keyName, value := range obj.Seq() {
|
|
keySetValue := reflect.New(ptrType.Key()).Elem()
|
|
switch ptrType.Key().Kind() {
|
|
case reflect.String:
|
|
keySetValue.SetString(keyName)
|
|
case reflect.Bool:
|
|
boolV, err := strconv.ParseBool(keyName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keySetValue.SetBool(boolV)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
intV, err := strconv.ParseInt(keyName, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keySetValue.SetInt(intV)
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
intV, err := strconv.ParseUint(keyName, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keySetValue.SetUint(intV)
|
|
case reflect.Float32, reflect.Float64:
|
|
floatV, err := strconv.ParseFloat(keyName, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
keySetValue.SetFloat(floatV)
|
|
}
|
|
|
|
valueOf := reflect.New(ptrType.Elem()).Elem()
|
|
if err := valueFrom(value, valueOf); err != nil {
|
|
return err
|
|
}
|
|
goMap.SetMapIndex(keySetValue, valueOf)
|
|
}
|
|
ptr.Set(goMap)
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("cannot set Object to %s", ptr.Kind())
|
|
}
|
|
default:
|
|
println(typeOf.String())
|
|
}
|
|
|
|
return nil
|
|
}
|