ccxt-go/exchange_helpers.go
zhangkun9038@dingtalk.com 1a2ce7046a first add
2025-02-28 10:33:20 +08:00

2775 lines
64 KiB
Go

package ccxt
import (
"encoding/json"
"fmt"
"math"
"reflect"
"runtime"
"strconv"
"strings"
"sync"
"time"
)
func Add(a interface{}, b interface{}) interface{} {
if (a == nil) || (b == nil) {
return nil
}
switch aType := a.(type) {
case int:
if bType, ok := b.(int); ok {
return aType + bType // Add as integers
}
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
res := aFloat + bFloat
if IsInteger(res) {
return ParseInt(res)
}
return res
case int64:
if bType, ok := b.(int64); ok {
return aType + bType // Add as integers
}
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
res := aFloat + bFloat
if IsInteger(res) {
return ParseInt(res)
}
return res
case float64:
bType := ToFloat64(b)
if bType == math.NaN() {
return nil
}
res := aType + bType
if IsInteger(res) {
return ParseInt(res)
}
case string:
if bType, ok := b.(string); ok {
return aType + bType // Concatenate as strings
}
}
return nil
}
func IsTrue(a interface{}) bool {
return EvalTruthy(a)
}
// EvalTruthy determines if a single interface value is truthy.
func EvalTruthy(val interface{}) bool {
if val == nil {
return false
}
// Check types of val
switch v := val.(type) {
case int, int32, int64, uint, uint32, uint64:
return v != 0
case float32, float64:
return v != 0.0
case string:
return v != ""
case bool:
return v // bool is already truthy or falsy
case []interface{}:
return len(v) > 0
case map[string]interface{}:
return len(v) > 0
case []string:
return len(v) > 0
case []int64:
return len(v) > 0
case []float64:
return len(v) > 0
case []bool:
return len(v) > 0
case []int:
return len(v) > 0
default:
return true
// Use reflection for other complex types (slices, maps, pointers, etc.)
// valType := reflect.TypeOf(val)
// switch valType.Kind() {
// case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Chan, reflect.Func:
// return !reflect.ValueOf(val).IsNil()
// }
}
return true // Consider non-nil complex types as truthy
}
// func IsInteger(value interface{}) bool {
// switch value.(type) {
// case int, int8, int16, int32, int64:
// return true
// case uint, uint8, uint16, uint32, uint64:
// return true
// default:
// return false
// }
// }
func IsInteger(value interface{}) bool {
switch v := value.(type) {
case int, int8, int16, int32, int64:
return true
case uint, uint8, uint16, uint32, uint64:
return true
case float32, float64:
// Check if the float has no fractional part
return v == math.Trunc(v.(float64))
default:
// // Handle other numeric types, including when value is a pointer to an int type
// val := reflect.ValueOf(value)
// if val.Kind() == reflect.Ptr {
// elem := val.Elem()
// if elem.IsValid() && elem.Kind() >= reflect.Int && elem.Kind() <= reflect.Float64 {
// return float64(elem.Float()) == math.Trunc(elem.Float())
// }
// }
return false
}
}
func GetValue(collection interface{}, key interface{}) interface{} {
if collection == nil {
return nil
}
if key == nil {
return nil
}
keyNum := -1
keyStr, ok := key.(string)
if !ok {
keyNum64 := ParseInt(key)
if keyNum64 == math.MinInt64 {
return nil
}
keyNum = int(keyNum64)
}
_, isMap := collection.(map[string]interface{})
if isMap || keyNum != -1 {
switch v := collection.(type) {
case map[string]interface{}:
if !ok {
return nil
}
if val, ok := v[keyStr]; ok {
return val
}
return nil
case []interface{}:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case []string:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case []int64:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case []float64:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case []bool:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case []int:
if keyNum >= len(v) {
return nil
}
return v[keyNum]
case string:
if keyNum >= len(v) {
return nil
}
return string(v[keyNum])
}
}
// this is needed in checkRequiredCredentials or alike
reflectValue := reflect.ValueOf(collection)
if reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
if reflectValue.Kind() == reflect.Struct {
stringKey := key.(string)
stringKeyCapitalized := Capitalize(stringKey)
field := reflectValue.FieldByName(stringKey)
fieldCapitalized := reflectValue.FieldByName(stringKeyCapitalized)
if fieldCapitalized.IsValid() {
return fieldCapitalized.Interface()
}
if field.IsValid() {
return field.Interface()
}
return nil
}
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
// Handle slice or array: key should be an integer index.
index2 := ParseInt(key)
if index2 == math.MinInt64 {
return nil // Key is not an int, invalid index
}
index := int(index2)
if index < 0 || index >= reflectValue.Len() {
return nil // Index out of bounds
}
return reflectValue.Index(index).Interface()
case reflect.Map:
// Handle map: key needs to be appropriate for the map
keyStr, ok := key.(string)
if !ok {
return nil // Key is not a string, invalid key
}
reflectKeyValue := reflect.ValueOf(keyStr)
if reflectValue.MapIndex(reflectKeyValue).IsValid() {
return reflectValue.MapIndex(reflectKeyValue).Interface()
}
return nil
default:
// Type not supported
return nil
}
}
func Multiply(a, b interface{}) interface{} {
if (a == nil) || (b == nil) {
return nil
}
aVal := reflect.ValueOf(a)
bVal := reflect.ValueOf(b)
// Ensure both values are numeric
if !aVal.IsValid() || !bVal.IsValid() || !aVal.Type().ConvertibleTo(bVal.Type()) {
return nil
}
// Convert a to the type of b to simplify multiplication
aValConverted := aVal.Convert(bVal.Type())
// Perform multiplication based on the kind of b
switch bVal.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return aValConverted.Int() * bVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return aValConverted.Uint() * bVal.Uint()
case reflect.Float32, reflect.Float64:
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
res := aFloat * bFloat
if IsInteger(res) {
return ParseInt(res)
}
return res
default:
return nil
}
}
func Divide(a, b interface{}) interface{} {
if a == nil || b == nil {
return nil
}
aVal := reflect.ValueOf(a)
bVal := reflect.ValueOf(b)
if !aVal.IsValid() || !bVal.IsValid() || !aVal.Type().ConvertibleTo(bVal.Type()) {
return nil
}
aValConverted := aVal.Convert(bVal.Type())
switch bVal.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if bVal.Int() == 0 {
return nil // Avoid division by zero
}
return aValConverted.Int() / bVal.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if bVal.Uint() == 0 {
return nil // Avoid division by zero
}
return aValConverted.Uint() / bVal.Uint()
case reflect.Float32, reflect.Float64:
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
if bFloat == 0.0 {
return nil // Avoid division by zero
}
res := aFloat / bFloat
if IsInteger(res) {
return ParseInt(res)
}
return res
default:
return nil
}
}
func Subtract(a, b interface{}) interface{} {
if a == nil || b == nil {
return nil
}
// aVal := reflect.ValueOf(a)
// bVal := reflect.ValueOf(b)
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
res := aFloat - bFloat
if IsInteger(res) {
return ParseInt(res)
}
return res
// if !aVal.IsValid() || !bVal.IsValid() || !aVal.Type().ConvertibleTo(bVal.Type()) {
// return nil
// }
// aValConverted := aVal.Convert(bVal.Type())
// switch bVal.Kind() {
// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// return aValConverted.Int() - bVal.Int()
// case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// return aValConverted.Uint() - bVal.Uint()
// case reflect.Float32, reflect.Float64:
// aFloat := ToFloat64(a)
// bFloat := ToFloat64(b)
// res := aFloat - bFloat
// if IsInteger(res) {
// return ParseInt(res)
// }
// return res
// default:
// return nil
// }
}
type Dict map[string]interface{}
// GetArrayLength returns the length of various array or slice types or string length.
func GetArrayLength(value interface{}) int {
if value == nil {
return 0
}
switch v := value.(type) {
case []interface{}:
return len(v)
case []string:
return len(v)
case []int64:
return len(v)
case []float64:
return len(v)
case []bool:
return len(v)
case []int:
return len(v)
case string:
return len(v) // should we do it here?
}
// val := reflect.ValueOf(value)
// switch val.Kind() {
// case reflect.Slice, reflect.Array:
// return val.Len()
// case reflect.String:
// return val.Len()
// case reflect.Map:
// // Specific check for a map type similar to List<dict> in C#
// if _, ok := value.(Dict); ok {
// return len(value.(Dict))
// }
// }
return 0
}
func IsGreaterThan(a, b interface{}) bool {
if a != nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
// Handle int, int64, float64 comparisons
switch aVal := a.(type) {
case int:
switch bVal := b.(type) {
case int:
return aVal > bVal
case int64:
return int64(aVal) > bVal
case float64:
return float64(aVal) > bVal
}
case int64:
switch bVal := b.(type) {
case int:
return aVal > int64(bVal)
case int64:
return aVal > bVal
case float64:
return float64(aVal) > bVal
}
case float64:
switch bVal := b.(type) {
case int:
return aVal > float64(bVal)
case int64:
return aVal > float64(bVal)
case float64:
return aVal > bVal
}
}
// If types cannot be compared, return false
return false
}
// aVal, bVal, ok := NormalizeAndConvert(a, b)
// if !ok {
// return false
// }
// switch aVal.Kind() {
// case reflect.Int, reflect.Int64:
// return aVal.Int() > bVal.Int()
// case reflect.Float64:
// return aVal.Float() > bVal.Float()
// case reflect.String:
// return aVal.String() > bVal.String()
// default:
// return false
// }
// }
// IsLessThan checks if a is less than b
func IsLessThan(a, b interface{}) bool {
return !IsGreaterThan(a, b) && !IsEqual(a, b)
}
// IsGreaterThanOrEqual checks if a is greater than or equal to b
func IsGreaterThanOrEqual(a, b interface{}) bool {
return IsGreaterThan(a, b) || IsEqual(a, b)
}
// IsLessThanOrEqual checks if a is less than or equal to b
func IsLessThanOrEqual(a, b interface{}) bool {
return IsLessThan(a, b) || IsEqual(a, b)
}
// Mod performs a modulus operation on a and b
func Mod(a, b interface{}) interface{} {
if a == nil || b == nil {
return nil
}
aFloat := ToFloat64(a)
bFloat := ToFloat64(b)
if aFloat == math.NaN() || bFloat == math.NaN() {
return nil
}
res := math.Mod(aFloat, bFloat)
if IsInteger(res) {
return ParseInt(res)
}
return res
// aVal, bVal, ok := NormalizeAndConvert(a, b)
// if !ok || bVal.Float() == 0 {
// return nil
// }
// return float64(int(aVal.Float()) % int(bVal.Float()))
}
// IsEqual checks for equality of a and b with dynamic type support
func IsEqual(a, b interface{}) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
if (a == true && b == false) || (a == false && b == true) {
return false
}
if (a == true && b == true) || (a == false && b == false) {
return true
}
// // aVal, bVal, ok := NormalizeAndConvert(a, b)
// if !ok {
// return false
// }
switch aVal := a.(type) {
case int:
switch bVal := b.(type) {
case int:
return aVal == bVal
case int64:
return int64(aVal) == bVal
case float64:
return float64(aVal) == bVal
}
case int64:
switch bVal := b.(type) {
case int:
return aVal == int64(bVal)
case int64:
return aVal == bVal
case float64:
return float64(aVal) == bVal
}
case float64:
switch bVal := b.(type) {
case int:
return aVal == float64(bVal)
case int64:
return aVal == float64(bVal)
case float64:
return aVal == bVal
}
case string:
if bVal, ok := b.(string); ok {
return aVal == bVal
}
}
// If types don't match or aren't handled, return false
return false
// switch aVal.Kind() {
// case reflect.Int, reflect.Int64:
// return aVal.Int() == bVal.Int()
// case reflect.Float64:
// return aVal.Float() == bVal.Float()
// case reflect.String:
// return aVal.String() == bVal.String()
// default:
// return false
// }
}
// NormalizeAndConvert normalizes and attempts to convert a and b to a common type
func NormalizeAndConvert(a, b interface{}) (reflect.Value, reflect.Value, bool) {
aVal := reflect.ValueOf(a)
bVal := reflect.ValueOf(b)
if aVal.Kind() != bVal.Kind() {
if aVal.Kind() < bVal.Kind() {
aVal = reflect.ValueOf(ToFloat64(a))
bVal = reflect.ValueOf(ToFloat64(b))
} else {
bVal = reflect.ValueOf(ToFloat64(b))
aVal = reflect.ValueOf(ToFloat64(a))
}
}
return aVal, bVal, true
}
func ToFloat64(v interface{}) float64 {
var result float64 = math.NaN()
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Int, reflect.Int64:
result = float64(val.Int())
case reflect.Float64:
result = val.Float()
case reflect.String:
result, err := strconv.ParseFloat(val.String(), 64)
if err == nil {
return result
}
}
return result
}
func Increment(a interface{}) interface{} {
switch v := a.(type) {
case int:
return v + 1
case int64:
return v + 1
case float64:
return v + 1.0
case string:
return v + "1"
default:
return nil
}
}
// Decrement decreases the numeric value by 1.
func Decrement(a interface{}) interface{} {
switch v := a.(type) {
case int:
return v - 1
case int64:
return v - 1
case float64:
return v - 1.0
default:
return nil
}
}
// Negate negates the numeric value.
func Negate(a interface{}) interface{} {
switch v := a.(type) {
case int:
return -v
case int64:
return -v
case float64:
return -v
default:
return nil
}
}
// UnaryPlus returns the numeric value unchanged.
func UnaryPlus(a interface{}) interface{} {
switch v := a.(type) {
case int:
return +v
case int64:
return +v
case float64:
return +v
default:
return nil
}
}
// PlusEqual adds the value of `value` to `a`, handling some basic types.
func PlusEqual(a, value interface{}) interface{} {
aVal := reflect.ValueOf(a)
valueVal := reflect.ValueOf(value)
if aVal.Kind() != valueVal.Kind() {
return nil // type mismatch
}
switch aVal.Kind() {
case reflect.Int, reflect.Int64:
return aVal.Int() + valueVal.Int()
case reflect.Float64:
return aVal.Float() + valueVal.Float()
case reflect.String:
return aVal.String() + valueVal.String()
default:
return nil
}
}
// func AppendToArray(slicePtr *interface{}, element interface{}) {
// array := (*slicePtr).([]interface{})
// *slicePtr = append(array, element)
// }
func AppendToArray(slicePtr *interface{}, element interface{}) {
switch array := (*slicePtr).(type) {
case []interface{}:
*slicePtr = append(array, element)
case []string:
if strElement, ok := element.(string); ok {
*slicePtr = append(array, strElement)
} else {
// Handle the case where the element is not a string if needed
// fmt.Println("Error: element is not a string")
}
default:
// fmt.Println("Error: Unsupported slice type")
}
}
// without reflection
func AddElementToObject(arrayOrDict interface{}, stringOrInt interface{}, value interface{}) {
switch obj := arrayOrDict.(type) {
case []string:
if index, ok := stringOrInt.(int); ok {
if index >= 0 && index < len(obj) {
obj[index] = fmt.Sprintf("%v", value)
// return nil
} else {
// return fmt.Errorf("index out of range")
}
} else {
// return fmt.Errorf("invalid key type for slice: expected int")
}
case []int:
if index, ok := stringOrInt.(int); ok {
if index >= 0 && index < len(obj) {
if v, ok := value.(int); ok {
obj[index] = v
// return nil
} else {
// return fmt.Errorf("value type mismatch for slice of int")
}
} else {
// return fmt.Errorf("index out of range")
}
} else {
// return fmt.Errorf("invalid key type for slice: expected int")
}
case []interface{}:
if index, ok := stringOrInt.(int); ok {
if index >= 0 && index < len(obj) {
obj[index] = value
// return nil
} else {
// return fmt.Errorf("index out of range")
}
} else {
// return fmt.Errorf("invalid key type for slice: expected int")
}
case []int64:
if index, ok := stringOrInt.(int); ok {
if index >= 0 && index < len(obj) {
if v, ok := value.(int64); ok {
obj[index] = v
// return nil
} else {
// return fmt.Errorf("value type mismatch for slice of int64")
}
} else {
// return fmt.Errorf("index out of range")
}
} else {
// return fmt.Errorf("invalid key type for slice: expected int")
}
case []float64:
if index, ok := stringOrInt.(int); ok {
if index >= 0 && index < len(obj) {
if v, ok := value.(float64); ok {
obj[index] = v
// return nil
} else {
// return fmt.Errorf("value type mismatch for slice of float64")
}
} else {
// return fmt.Errorf("index out of range")
}
} else {
// return fmt.Errorf("invalid key type for slice: expected int")
}
case map[string]interface{}:
if key, ok := stringOrInt.(string); ok {
obj[key] = value
// return nil
} else {
// return fmt.Errorf("invalid key type for map: expected string")
}
default:
// return fmt.Errorf("unsupported type: %T", arrayOrDict)
}
}
// func AddElementToObject(arrayOrDict interface{}, stringOrInt interface{}, value interface{}) {
// val := reflect.ValueOf(arrayOrDict)
// key := reflect.ValueOf(stringOrInt)
// valueVal := reflect.ValueOf(value)
// switch val.Kind() {
// case reflect.Slice:
// if key.Kind() != reflect.Int {
// // return fmt.Errorf("index must be an integer for slices")
// }
// index := int(key.Int())
// if index < 0 || index >= val.Len() {
// // return fmt.Errorf("index out of range")
// }
// val.Index(index).Set(valueVal)
// case reflect.Map:
// if !key.Type().AssignableTo(val.Type().Key()) {
// // return fmt.Errorf("key type %s does not match map key type %s", key.Type(), val.Type().Key())
// }
// // if !valueVal.Type().AssignableTo(val.Type().Elem()) {
// // // return fmt.Errorf("value type %s does not match map value type %s", valueVal.Type(), val.Type().Elem())
// // }
// // fmt.Println("key", key.Interface())
// // fmt.Println("value", valueVal.Interface())
// if !valueVal.IsValid() {
// val.SetMapIndex(key, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// val.SetMapIndex(key, valueVal)
// }
// default:
// // return fmt.Errorf("unsupported type: %s", val.Kind())
// }
// // return nil
// }
func InOp(dict interface{}, key interface{}) bool {
if dict == nil {
return false
}
if key == nil {
return false
}
if IsNumber(key) {
return false
}
switch v := dict.(type) {
case map[string]interface{}:
if _, ok := v[key.(string)]; ok {
return true
}
}
return false
// dictVal := reflect.ValueOf(dict)
// // Ensure that the provided dict is a map
// if dictVal.Kind() != reflect.Map {
// return false
// }
// keyVal := reflect.ValueOf(key)
// // Check if the map has the provided key todo:debug here
// if dictVal.MapIndex(keyVal).IsValid() {
// return true
// }
// return false
}
// func InOp(dict interface{}, key interface{}) bool {
// if dict == nil {
// return false
// }
// if key == nil {
// return false
// }
// if IsNumber(key) {
// return false
// }
// switch v := dict.(type) {
// case map[string]interface{}:
// if _, ok := v[key.(string)]; ok {
// return true
// }
// }
// return false
// // dictVal := reflect.ValueOf(dict)
// // // Ensure that the provided dict is a map
// // if dictVal.Kind() != reflect.Map {
// // return false
// // }
// // keyVal := reflect.ValueOf(key)
// // // Check if the map has the provided key todo:debug here
// // if dictVal.MapIndex(keyVal).IsValid() {
// // return true
// // }
// // return false
// }
func GetIndexOf(str interface{}, target interface{}) int {
switch v := str.(type) {
case []string:
t, ok := target.(string)
if !ok {
return -1
}
for i, s := range v {
if s == t {
return i
}
}
case []int:
t, ok := target.(int)
if !ok {
return -1
}
for i, n := range v {
if n == t {
return i
}
}
case string:
t, ok := target.(string)
if !ok {
return -1
}
return strings.Index(v, t)
}
return -1
}
// IsBool checks if the input is a boolean
func IsBool(v interface{}) bool {
if v == nil {
return false
}
_, ok := v.(bool)
return ok
}
// IsDictionary checks if the input is a map (dictionary in Python)
func IsDictionary(v interface{}) bool {
if v == nil {
return false
}
switch v.(type) {
case map[string]interface{}:
return true
case Dict:
return true
case map[interface{}]interface{}:
return true
default:
return false
}
// return reflect.TypeOf(v).Kind() == reflect.Map
}
// IsString checks if the input is a string
func IsString(v interface{}) bool {
if v == nil {
return false
}
_, ok := v.(string)
return ok
}
// IsInt checks if the input is an integer
func IsInt(v interface{}) bool {
if v == nil {
return false
}
switch v.(type) {
case int, int8, int16, int32, int64:
return true
case uint, uint8, uint16, uint32, uint64:
return true
default:
return false
}
}
// IsFunction checks if the input is a function
func IsFunction(v interface{}) bool {
if v == nil {
return false
}
return reflect.TypeOf(v).Kind() == reflect.Func
}
func IsNumber(v interface{}) bool {
if v == nil {
return false
}
switch v.(type) {
case int, int8, int16, int32, int64:
return true
case uint, uint8, uint16, uint32, uint64:
return true
case float32, float64:
return true
default:
return false
}
}
func IsObject(v interface{}) bool {
if v == nil {
return false
}
kind := reflect.TypeOf(v).Kind()
switch kind {
case reflect.Array, reflect.Chan, reflect.Func, reflect.Interface,
reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.UnsafePointer:
return true
default:
return false
}
}
func ToLower(v interface{}) string {
if str, ok := v.(string); ok {
return strings.ToLower(str)
}
return ""
}
// ToUpper converts a string to uppercase
func ToUpper(v interface{}) string {
if str, ok := v.(string); ok {
return strings.ToUpper(str)
}
return ""
}
// IsInt checks if the input is an integer
// func IsInt(v interface{}) bool {
// switch v.(type) {
// case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
// return true
// default:
// return false
// }
// }
// MathFloor returns the largest integer less than or equal to the given number
func MathFloor(v interface{}) float64 {
if num, ok := v.(float64); ok {
return math.Floor(num)
}
return 0
}
// MathCeil returns the smallest integer greater than or equal to the given number
func MathCeil(v interface{}) float64 {
if num, ok := v.(float64); ok {
return math.Ceil(num)
}
if num, ok := v.(int); ok {
return math.Ceil(float64(num))
}
if num, ok := v.(int64); ok {
return math.Ceil(float64(num))
}
return 0
}
// MathRound returns the nearest integer, rounding half away from zero
func MathRound(v interface{}) float64 {
if num, ok := v.(float64); ok {
return math.Round(num)
}
if num, ok := v.(int); ok {
return math.Round(float64(num))
}
if num, ok := v.(int64); ok {
return math.Round(float64(num))
}
return 0
}
// StartsWith checks if the string starts with the specified prefix
func StartsWith(v interface{}, prefix interface{}) bool {
if str, ok := v.(string); ok {
prefixStr := ToString(prefix)
return strings.HasPrefix(str, prefixStr)
}
return false
}
// EndsWith checks if the string ends with the specified suffix
func EndsWith(v interface{}, suffix interface{}) bool {
if str, ok := v.(string); ok {
suffixStr := ToString(suffix)
return strings.HasSuffix(str, suffixStr)
}
return false
}
// IndexOf returns the index of the first occurrence of a substring
func IndexOf(v interface{}, substr interface{}) int {
if str, ok := v.(string); ok {
substrStr := ToString(substr)
return strings.Index(str, substrStr)
}
return -1
}
// Trim removes leading and trailing whitespace from a string
func Trim(v interface{}) string {
if str, ok := v.(string); ok {
return strings.TrimSpace(str)
}
return ""
}
// Contains checks if the string contains the specified substring
func Contains(v interface{}, substr interface{}) bool {
if str, ok := v.(string); ok {
substrStr := ToString(substr)
return strings.Contains(str, substrStr)
}
return false
}
func ToString(v interface{}) string {
switch v := v.(type) {
case string:
return v
case int, int8, int16, int32, int64:
return fmt.Sprintf("%d", v)
case uint, uint8, uint16, uint32, uint64:
return fmt.Sprintf("%d", v)
case float32, float64:
convertedValue := ToFloat64(v)
if convertedValue == math.Trunc(convertedValue) {
return fmt.Sprintf("%d", int(convertedValue))
}
return fmt.Sprintf("%f", v)
case bool:
return fmt.Sprintf("%t", v)
default:
// Handle maps, slices, and functions using reflection
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.Map:
result := "{"
for _, key := range val.MapKeys() {
result += fmt.Sprintf("%v: %v, ", ToString(key.Interface()), ToString(val.MapIndex(key).Interface()))
}
if len(result) > 1 {
result = result[:len(result)-2] // Remove trailing comma and space
}
result += "}"
return result
case reflect.Slice, reflect.Array:
result := "["
for i := 0; i < val.Len(); i++ {
result += fmt.Sprintf("%v, ", ToString(val.Index(i).Interface()))
}
if len(result) > 1 {
result = result[:len(result)-2] // Remove trailing comma and space
}
result += "]"
return result
case reflect.Func:
return fmt.Sprintf("Function: %v", val.Type().String())
default:
return fmt.Sprintf("%v", v)
}
}
}
func Join(slice interface{}, sep interface{}) string {
sepStr := ToString(sep)
var strSlice []string
switch v := slice.(type) {
case []string:
strSlice = v
case []interface{}:
for _, elem := range v {
strSlice = append(strSlice, ToString(elem))
}
default:
return ""
}
return strings.Join(strSlice, sepStr)
}
// Split splits a string into a slice of substrings separated by a separator
func Split(str interface{}, sep interface{}) []string {
strVal, ok := str.(string)
if !ok {
return nil
}
sepStr := ToString(sep)
return strings.Split(strVal, sepStr)
}
// ObjectKeys returns the keys of a map as a slice of strings
func ObjectKeys(v interface{}) []string {
if v == nil {
return nil
}
if mapObject, ok := v.(map[string]interface{}); ok {
keys := make([]string, 0, len(mapObject))
for key := range mapObject {
keys = append(keys, key)
}
return keys
}
return nil
// val := reflect.ValueOf(v)
// if val.Kind() != reflect.Map {
// return nil
// }
// keys := val.MapKeys()
// strKeys := make([]string, len(keys))
// for i, key := range keys {
// strKeys[i] = ToString(key.Interface())
// }
// return strKeys
}
// ObjectValues returns the values of a map as a slice of interface{}
func ObjectValues(v interface{}) []interface{} {
// return values
if mapObject, ok := v.(map[string]interface{}); ok {
values := make([]interface{}, 0, len(mapObject))
for _, value := range mapObject {
values = append(values, value)
}
return values
}
return nil
// val := ref
}
func JsonParse(jsonStr2 interface{}) interface{} {
jsonStr := jsonStr2.(string)
var result interface{}
err := json.Unmarshal([]byte(jsonStr), &result)
if err != nil {
return nil
}
return result
}
func IsArray(v interface{}) bool {
if v == nil {
return false
}
switch v.(type) {
case []interface{}:
return true
case []string, []bool:
return true
case []int, []int8, []int16, []int32, []int64, []float32, []float64, []uint, []uint8, []uint16, []uint32, []uint64:
return true
default:
return false
// kind := reflect.TypeOf(v).Kind()
// return kind == reflect.Slice || kind == reflect.Array
}
}
func Shift(slice interface{}) (interface{}, interface{}) {
sliceVal, ok := castToSlice(slice)
if !ok || len(sliceVal) == 0 {
return slice, nil
}
return sliceVal[1:], sliceVal[0]
}
// Reverse reverses the elements of a slice in place
func Reverse(slice interface{}) {
sliceVal, ok := castToSlice(slice)
if !ok {
panic("provided value is not a slice")
}
// Reverse the elements in place
for i, j := 0, len(sliceVal)-1; i < j; i, j = i+1, j-1 {
sliceVal[i], sliceVal[j] = sliceVal[j], sliceVal[i]
}
// Copy the reversed values back into the original slice
// Since Go is a pass-by-value language, we need to reflect to modify the original slice in place
v := reflect.ValueOf(slice)
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(reflect.ValueOf(sliceVal[i]))
}
}
// Pop removes the last element from a slice and returns the new slice and the removed element
func Pop(slice interface{}) (interface{}, interface{}) {
sliceVal, ok := castToSlice(slice)
if !ok || len(sliceVal) == 0 {
return slice, nil
}
return sliceVal[:len(sliceVal)-1], sliceVal[len(sliceVal)-1]
}
func CastToSlice(slice interface{}) ([]interface{}, bool) {
return castToSlice(slice)
}
// Helper function to cast interface{} to []interface{}
func castToSlice(slice interface{}) ([]interface{}, bool) {
val := reflect.ValueOf(slice)
if val.Kind() != reflect.Slice {
return nil, false
}
sliceVal := make([]interface{}, val.Len())
for i := 0; i < val.Len(); i++ {
sliceVal[i] = val.Index(i).Interface()
}
return sliceVal, true
}
func Replace(input interface{}, old interface{}, new interface{}) string {
str := ToString(input)
oldStr := ToString(old)
newStr := ToString(new)
return strings.ReplaceAll(str, oldStr, newStr)
}
// PadEnd pads the input string on the right with padStr until it reaches the specified length
func PadEnd(input interface{}, length2 interface{}, padStr interface{}) string {
length := int(ParseInt(length2))
str := ToString(input)
pad := ToString(padStr)
for len(str) < length {
str += pad
}
return str[:length]
}
// PadStart pads the input string on the left with padStr until it reaches the specified length
func PadStart(input interface{}, length2 interface{}, padStr interface{}) string {
length := int(ParseInt(length2))
str := ToString(input)
pad := ToString(padStr)
for len(str) < length {
str = pad + str
}
return str[len(str)-length:]
}
// DateNow returns the current date and time as a string
func DateNow() string {
return time.Now().Format(time.RFC3339)
}
func GetLength(v interface{}) int {
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.String:
return len(val.String())
case reflect.Array, reflect.Slice:
return val.Len()
default:
return 0
}
}
func IsNil(x interface{}) bool {
// https://blog.devtrovert.com/p/go-secret-interface-nil-is-not-nil
if x == nil {
return true
}
return false
// switch val := x.(type){
// case interface{}:
// return val == nil
// case
// }
// value := reflect.ValueOf(x)
// kind := value.Kind()
// switch kind {
// case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
// return value.IsNil()
// default:
// return false
// }
}
func GetArg(v []interface{}, index int, def interface{}) interface{} {
if len(v) <= index {
return def
}
val := v[index]
if val == nil {
return def
}
if res, ok := val.([]interface{}); ok { // this is not working well with safeList(x, 'key', []) but works for fetchTrade(s, options interface{}...)
// if len(res) == 0 {
// return def
// }
if res == nil {
return def
}
}
// do we need this??
// if IsNil(val) { // check https://blog.devtrovert.com/p/go-secret-interface-nil-is-not-nil
// return def
// }
return val
}
func Ternary(cond bool, whenTrue interface{}, whenFalse interface{}) interface{} {
if cond {
return whenTrue
}
return whenFalse
}
func IsInstance(value interface{}, typ interface{}) bool {
// Get the reflect.Type of the value and the type
if s, ok := value.(string); ok {
isError := strings.HasPrefix(s, "panic")
if isError {
value := reflect.ValueOf(typ)
funcName := ""
if value.Kind() == reflect.Func {
funcName = runtime.FuncForPC(value.Pointer()).Name()
// Extract only the function name by removing the package path
parts := strings.Split(funcName, ".")
funcName = parts[len(parts)-1]
}
return strings.Contains(s, funcName)
}
}
valueType := reflect.TypeOf(value)
typeType := reflect.TypeOf(typ)
// Compare the two types
return valueType == typeType
}
func Slice(str2 interface{}, idx1 interface{}, idx2 interface{}) string {
if str2 == nil {
return ""
}
str := str2.(string)
var start int64 = -1
if idx1 != nil {
start = ParseInt(idx1)
}
var lenStr int64 = int64(len(str))
if idx2 == nil {
if start < 0 {
innerStart := lenStr + start
if innerStart < 0 {
innerStart = 0
}
return str[innerStart:]
}
return str[start:]
} else {
end := ParseInt(idx2)
if start < 0 {
start = lenStr + start
}
if end < 0 {
end = lenStr + end
}
if end > lenStr {
end = lenStr
}
return str[start:end]
}
}
type Task func() interface{}
func PromiseAll(tasksInterface interface{}) <-chan interface{} {
return promiseAll(tasksInterface)
}
func promiseAll(tasksInterface interface{}) <-chan interface{} {
ch := make(chan interface{})
panicChan := make(chan interface{}, 1) // Separate channel for panics
var once sync.Once // Ensure only one message is sent to ch
go func() {
defer close(ch)
defer ReturnPanicError(ch)
// Ensure tasksInterface is a slice of channels (<-chan interface{})
tasks, ok := tasksInterface.([]interface{})
if !ok {
ch <- nil // Return nil if the input is not a slice of interfaces
return
}
results := make([]interface{}, len(tasks))
var wg sync.WaitGroup
var resultsLock sync.Mutex
wg.Add(len(tasks))
for i, task := range tasks {
go func(i int, task interface{}) {
defer wg.Done()
// Capture panic and send to panicChan directly
defer func() {
if r := recover(); r != nil {
if r != "break" {
once.Do(func() { ch <- "panic:" + ToString(r) })
}
}
}()
// Assert the task is a channel
if chanTask, ok := task.(<-chan interface{}); ok {
// Receive the result from the channel
result := <-chanTask
resultsLock.Lock()
results[i] = result
resultsLock.Unlock()
} else {
// If the task is not a channel, set the result to nil
resultsLock.Lock()
results[i] = nil
resultsLock.Unlock()
}
}(i, task)
}
// Wait for all tasks to complete
wg.Wait()
close(panicChan)
// If no panics occurred, send the results
once.Do(func() { ch <- results })
}()
return ch
}
// func promiseAll(tasksInterface interface{}) <-chan interface{} {
// ch := make(chan interface{})
// panicChan := make(chan interface{}, 1) // Separate channel for panics
// go func() {
// defer close(ch)
// // Ensure tasksInterface is a slice of channels (<-chan interface{})
// tasks, ok := tasksInterface.([]interface{})
// if !ok {
// ch <- nil // Return nil if the input is not a slice of interfaces
// return
// }
// results := make([]interface{}, len(tasks))
// var wg sync.WaitGroup
// wg.Add(len(tasks))
// for i, task := range tasks {
// go func(i int, task interface{}) {
// defer wg.Done()
// defer ReturnPanicError(panicChan)
// // Assert the task is a channel
// if chanTask, ok := task.(<-chan interface{}); ok {
// // Receive the result from the channel
// results[i] = <-chanTask
// } else {
// // If the task is not a channel, set the result to nil
// results[i] = nil
// }
// }(i, task)
// }
// // Wait for all tasks to complete
// wg.Wait()
// close(panicChan)
// // Check if any panics occurred and report the first one
// select {
// case panicMsg := <-panicChan:
// ch <- panicMsg // Send the panic message
// default:
// ch <- results // No panics, send results
// }
// }()
// return ch
// }
// func promiseAll(tasksInterface interface{}) <-chan interface{} {
// ch := make(chan interface{})
// go func() {
// defer close(ch)
// defer func() {
// if r := recover(); r != nil {
// if r != "break" {
// ch <- "panic:" + ToString(r)
// }
// }
// }()
// // Ensure tasksInterface is a slice of channels (<-chan interface{})
// tasks, ok := tasksInterface.([]interface{})
// if !ok {
// ch <- nil // Return nil if the input is not a slice of interfaces
// return
// }
// results := make([]interface{}, len(tasks))
// var wg sync.WaitGroup
// wg.Add(len(tasks))
// // A separate channel to capture panics
// panicChan := make(chan string, len(tasks))
// for i, task := range tasks {
// go func(i int, task interface{}) {
// defer wg.Done()
// defer func() {
// if r := recover(); r != nil {
// if r != "break" {
// panicChan <- "panic:" + ToString(r)
// }
// }
// }()
// // Assert the task is a channel
// if chanTask, ok := task.(<-chan interface{}); ok {
// // Receive the result from the channel
// results[i] = <-chanTask
// } else {
// // If the task is not a channel, set the result to nil
// results[i] = nil
// }
// }(i, task)
// }
// // Wait for all tasks to complete
// wg.Wait()
// close(panicChan)
// // Check if any panics occurred and report the first one
// select {
// case panicMsg := <-panicChan:
// ch <- panicMsg // Send the panic message
// default:
// ch <- results // No panics, send results
// }
// }()
// return ch
// }
// func promiseAll(tasksInterface interface{}) <-chan interface{} {
// ch := make(chan interface{})
// go func() {
// defer close(ch)
// defer func() {
// if r := recover(); r != nil {
// if r != "break" {
// ch <- "panic:" + ToString(r)
// }
// }
// }()
// // Ensure tasksInterface is a slice of channels (<-chan interface{})
// tasks, ok := tasksInterface.([]interface{})
// if !ok {
// ch <- nil // Return nil if the input is not a slice of interfaces
// return
// }
// results := make([]interface{}, len(tasks))
// var wg sync.WaitGroup
// wg.Add(len(tasks))
// for i, task := range tasks {
// go func(i int, task interface{}) {
// defer wg.Done()
// defer func() {
// if r := recover(); r != nil {
// if r != "break" {
// ch <- "panic:" + ToString(r)
// }
// }
// }()
// // Assert the task is a channel
// if chanTask, ok := task.(<-chan interface{}); ok {
// // Receive the result from the channel
// results[i] = <-chanTask
// } else {
// // If the task is not a channel, set the result to nil
// results[i] = nil
// }
// }(i, task)
// }
// // Wait for all tasks to complete
// wg.Wait()
// // Once all tasks are done, send the results
// ch <- results
// }()
// return ch
// }
func ParseInt(number interface{}) int64 {
switch v := number.(type) {
case int:
return int64(v)
case int8:
return int64(v)
case int16:
return int64(v)
case int32:
return int64(v)
case int64:
return v
case uint:
return int64(v)
case uint8:
return int64(v)
case uint16:
return int64(v)
case uint32:
return int64(v)
// case uint64:
// if v <= uint64(^int64(0)) {
// return int64(v)
// }
case float32:
return int64(v)
case float64:
return int64(v)
case string:
if i, err := strconv.ParseInt(v, 10, 64); err == nil {
return i
}
}
return math.MinInt64 // Default value if conversion is not possible
}
func MathMin(a, b interface{}) interface{} {
return mathMin(a, b)
}
func mathMin(a, b interface{}) interface{} {
if a == nil || b == nil {
return nil
}
af := ToFloat64(a)
bf := ToFloat64(b)
if af == math.NaN() || bf == math.NaN() {
return nil
}
if af < bf {
return af
}
return bf
// switch a := a.(type) {
// case int:
// b := b.(int)
// if a < b {
// return a
// }
// return b
// case float64:
// b := b.(float64)
// if a < b {
// return a
// }
// return b
// case string:
// b := b.(string)
// if a < b {
// return a
// }
// return b
// default:
// return nil
// }
}
func MathMax(a, b interface{}) interface{} {
return mathMax(a, b)
}
// mathMax returns the maximum of two values of the same type.
// It supports int, float64, and string types.
func mathMax(a, b interface{}) interface{} {
if a == nil || b == nil {
return nil
}
af := ToFloat64(a)
bf := ToFloat64(b)
if af == math.NaN() || bf == math.NaN() {
return nil
}
if af > bf {
return af
}
return bf
}
// parseInt tries to convert various types of input to an int
// func parseInt(input interface{}) interface{} {
// switch v := input.(type) {
// case int:
// return v
// case int8:
// return int(v)
// case int16:
// return int(v)
// case int32:
// return int(v)
// case int64:
// return int(v)
// case uint:
// return int(v)
// case uint8:
// return int(v)
// case uint16:
// return int(v)
// case uint32:
// return int(v)
// case uint64:
// return int(v)
// case float32:
// return int(v)
// case float64:
// return int(v)
// case string:
// if result, err := strconv.Atoi(v); err == nil {
// return result
// }
// return nil
// default:
// return nil
// }
// }
// parseFloat tries to convert various types of input to a float64
func ParseFloat(input interface{}) interface{} {
switch v := input.(type) {
case float32:
return float64(v)
case float64:
return v
case int:
return float64(v)
case int8:
return float64(v)
case int16:
return float64(v)
case int32:
return float64(v)
case int64:
return float64(v)
case uint:
return float64(v)
case uint8:
return float64(v)
case uint16:
return float64(v)
case uint32:
return float64(v)
case uint64:
return float64(v)
case string:
if result, err := strconv.ParseFloat(v, 64); err == nil {
return result
}
return nil
default:
return nil
}
}
func ParseJSON(input interface{}) interface{} {
jsonString, ok := input.(string)
if !ok {
return nil
}
// // var result interface{}
// if jsonString[0] == '[' {
// var arrayResult []map[string]interface{}
// err := json.Unmarshal([]byte(jsonString), &arrayResult)
// if err != nil {
// return nil
// }
// return arrayResult
// }
// var mapResult map[string]interface{}
// err := json.Unmarshal([]byte(jsonString), &mapResult)
// if err != nil {
// return nil
// }
// return mapResult
var result interface{}
decoder := json.NewDecoder(strings.NewReader(jsonString))
decoder.UseNumber() // Ensures large numbers are handled correctly
err := decoder.Decode(&result)
if err != nil {
return nil
}
convertNumbers(result) //convert json.Number to int64
return result
}
func convertNumbers(data interface{}) {
switch v := data.(type) {
case map[string]interface{}:
for key, value := range v {
if number, ok := value.(json.Number); ok {
// Try to convert the json.Number to int64
if intVal, err := number.Int64(); err == nil {
v[key] = intVal
} else {
v[key] = number.String() // Preserve the string if not convertible to int64
}
} else {
convertNumbers(value) // Recurse for nested maps
}
}
case []interface{}:
for i, value := range v {
if number, ok := value.(json.Number); ok {
// Try to convert the json.Number to int64
if intVal, err := number.Int64(); err == nil {
v[i] = intVal
} else {
v[i] = number.String() // Preserve the string if not convertible to int64
}
} else {
convertNumbers(value) // Recurse for nested arrays
}
}
}
}
func throwDynamicException(exceptionType interface{}, message interface{}) {
functionError := exceptionType.(func(...interface{}) error)
errorMsg := functionError(message)
panic(errorMsg)
// to do implement
// // exceptionTypeStr, ok := exceptionType.(string)
// if !ok {
// panic("exceptionType must be a string representing the error type")
// // messageStr, ok := message.(string)
// // if !ok {
// // panic("message must be a string")
// // }
// // constructor, exists := customErrors[exceptionTypeStr]
// // if !exists {
// // panic(errors.New("unknown error type: " + exceptionTypeStr))
// // }
// // err := constructor(messageStr)
// // panic(err)
}
func OpNeg(value interface{}) interface{} {
val := reflect.ValueOf(value)
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return -val.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return nil // Cannot negate unsigned integers, return nil
case reflect.Float32, reflect.Float64:
return -val.Float()
case reflect.Complex64, reflect.Complex128:
return -val.Complex()
default:
return nil // Unsupported type, return nil
}
}
func JsonStringify(obj interface{}) string {
if obj == nil {
return ""
}
// Check if the object is an error (Go's equivalent of an exception)
if err, ok := obj.(error); ok {
// Create an anonymous struct with the error type name
errorObj := struct {
Name string `json:"name"`
}{
Name: reflect.TypeOf(err).Name(),
}
// Serialize the error object to JSON
jsonData, _ := json.Marshal(errorObj)
return string(jsonData)
}
// Serialize the object to JSON
jsonData, _ := json.Marshal(obj)
return string(jsonData)
}
func toFixed(number interface{}, decimals interface{}) float64 {
// Assert that the number is a float64 or convert it
num := ToFloat64(number)
// Assert that the decimals is an int or convert it
dec := ParseInt(decimals)
// Calculate the rounding multiplier
multiplier := math.Pow(10, float64(dec))
return math.Round(num*multiplier) / multiplier
}
func Remove(dict interface{}, key interface{}) {
// Attempt to cast the dict to map[string]interface{}
castedDict, ok := dict.(map[string]interface{})
if !ok {
// Panic if the cast fails
panic("provided value is not a map[string]interface{}")
}
// Attempt to cast the key to string
keyStr, ok := key.(string)
if !ok {
// Panic if the key is not a string
panic("provided key is not a string")
}
// Check if the key exists, panic if it doesn't
if _, exists := castedDict[keyStr]; !exists {
panic(fmt.Sprintf("key '%s' does not exist in the map", keyStr))
}
// Remove the key from the map
delete(castedDict, keyStr)
}
func Capitalize(s string) string {
if len(s) == 0 {
return s
}
// Convert the first letter to uppercase
firstLetter := strings.ToUpper(string(s[0]))
// Combine the uppercase first letter with the rest of the string
return firstLetter + s[1:]
}
func SetDefaults(p interface{}) {
setDefaults(p)
}
func setDefaults(p interface{}) {
// Get the value of the pointer to struct
val := reflect.ValueOf(p).Elem()
typ := val.Type()
// Iterate over the fields of the struct using reflection
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
fieldType := typ.Field(i)
if value, ok := fieldType.Tag.Lookup("default"); ok {
switch field.Kind() {
case reflect.String:
if field.String() == "" {
field.SetString(value)
}
case reflect.Int:
if field.Int() == 0 {
if intValue, err := strconv.Atoi(value); err == nil {
field.SetInt(int64(intValue))
}
}
// Add other types as necessary
}
}
}
}
// func CallInternalMethod(itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
// name := Capitalize(name2)
// baseValue := reflect.ValueOf(itf)
// baseType := baseValue.Type()
// ch := make(chan interface{})
// go func() {
// // Error handling
// defer func() {
// if r := recover(); r != nil {
// ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
// close(ch)
// }
// }()
// for i := 0; i < baseType.NumMethod(); i++ {
// method := baseType.Method(i)
// if name == method.Name {
// methodValue := baseValue.MethodByName(name)
// methodType := method.Type
// numIn := methodType.NumIn()
// isVariadic := methodType.IsVariadic()
// var in []reflect.Value
// // Handle fixed arguments for both regular and variadic functions
// for k := 0; k < numIn-1; k++ {
// if k < len(args) {
// if args[k] == nil {
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// } else {
// // paramType := methodType.In(k)
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// // in = append(in, reflect.Zero(paramType))
// }
// }
// // Properly handle the variadic arguments
// if isVariadic {
// variadicArgs := []reflect.Value{}
// // variadicType := methodType.In(numIn - 1).Elem() // Get the type of the variadic argument
// for k := numIn - 1; k < len(args); k++ {
// if args[k] == nil {
// variadicArgs = append(variadicArgs, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// variadicArgs = append(variadicArgs, reflect.ValueOf(args[k]))
// }
// }
// in = append(in, variadicArgs...)
// } else if len(args) >= numIn-1 {
// // Handle non-variadic arguments beyond fixed ones
// for k := numIn - 1; k < len(args); k++ {
// if args[k] == nil {
// // paramType := methodType.In(k)
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// }
// }
// // Call the method with the constructed arguments
// res := methodValue.Call(in)
// // Handle the result
// if len(res) > 0 && res[0].Kind() == reflect.Chan {
// resultChan := res[0]
// go func() {
// for {
// val, ok := resultChan.Recv()
// if !ok {
// break // result channel is closed
// }
// ch <- val.Interface() // pass the value to the output channel
// }
// close(ch) // close the output channel after all values are received
// }()
// return
// } else if len(res) > 0 {
// ch <- res[0].Interface()
// } else {
// ch <- nil
// }
// close(ch)
// return
// }
// }
// // If no method is found, return nil
// ch <- nil
// close(ch)
// }()
// return ch
// }
// var methodCache = sync.Map{}
// func CallInternalMethodCache(itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
// name := Capitalize(name2)
// baseValue := reflect.ValueOf(itf)
// baseType := baseValue.Type()
// // Cache key to avoid redundant reflection
// cacheKey := fmt.Sprintf("%T.%s", itf, name)
// cachedMethod, found := methodCache.Load(cacheKey)
// ch := make(chan interface{})
// go func() {
// defer func() {
// if r := recover(); r != nil {
// ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
// }
// close(ch)
// }()
// var method reflect.Method
// if found {
// method = cachedMethod.(reflect.Method)
// } else {
// for i := 0; i < baseType.NumMethod(); i++ {
// if baseType.Method(i).Name == name {
// method = baseType.Method(i)
// methodCache.Store(cacheKey, method)
// break
// }
// }
// if method.Name == "" {
// ch <- nil
// return
// }
// }
// methodValue := baseValue.MethodByName(name)
// methodType := method.Type
// numIn := methodType.NumIn()
// // isVariadic := methodType.IsVariadic()
// in := make([]reflect.Value, 0, numIn)
// // Handle arguments
// for k := 0; k < numIn; k++ {
// if k < len(args) {
// if args[k] == nil {
// in = append(in, reflect.Zero(methodType.In(k)))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// } else {
// in = append(in, reflect.Zero(methodType.In(k)))
// }
// }
// // Call method
// res := methodValue.Call(in)
// // Return result
// if len(res) > 0 {
// ch <- res[0].Interface()
// } else {
// ch <- nil
// }
// }()
// return ch
// }
// original imp
func CallInternalMethod3(itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
name := Capitalize(name2)
baseValue := reflect.ValueOf(itf)
baseType := baseValue.Type()
ch := make(chan interface{})
go func() {
// Error handling
defer func() {
if r := recover(); r != nil {
ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
close(ch)
}
}()
for i := 0; i < baseType.NumMethod(); i++ {
method := baseType.Method(i)
if name == method.Name {
methodValue := baseValue.MethodByName(name)
methodType := method.Type
numIn := methodType.NumIn()
isVariadic := methodType.IsVariadic()
var in []reflect.Value
// Handle fixed arguments for both regular and variadic functions
for k := 0; k < numIn-1; k++ {
if k < len(args) {
if args[k] == nil {
in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
} else {
in = append(in, reflect.ValueOf(args[k]))
}
} else {
// paramType := methodType.In(k)
in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// in = append(in, reflect.Zero(paramType))
}
}
// Properly handle the variadic arguments
if isVariadic {
variadicArgs := []reflect.Value{}
// variadicType := methodType.In(numIn - 1).Elem() // Get the type of the variadic argument
for k := numIn - 1; k < len(args); k++ {
if args[k] == nil {
variadicArgs = append(variadicArgs, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
} else {
variadicArgs = append(variadicArgs, reflect.ValueOf(args[k]))
}
}
in = append(in, variadicArgs...)
} else if len(args) >= numIn-1 {
// Handle non-variadic arguments beyond fixed ones
for k := numIn - 1; k < len(args); k++ {
if args[k] == nil {
// paramType := methodType.In(k)
in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
} else {
in = append(in, reflect.ValueOf(args[k]))
}
}
}
// Call the method with the constructed arguments
res := methodValue.Call(in)
// Handle the result
if len(res) > 0 && res[0].Kind() == reflect.Chan {
resultChan := res[0]
go func() {
for {
val, ok := resultChan.Recv()
if !ok {
break // result channel is closed
}
ch <- val.Interface() // pass the value to the output channel
}
close(ch) // close the output channel after all values are received
}()
return
} else if len(res) > 0 {
ch <- res[0].Interface()
} else {
ch <- nil
}
close(ch)
return
}
}
// If no method is found, return nil
ch <- nil
close(ch)
}()
return ch
}
func CallInternalMethod(methodCache *sync.Map, itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
name := Capitalize(name2)
// baseValue := reflect.ValueOf(itf)
// baseType := baseValue.Type()
// if !this.cacheLoaded {
// this.cacheLoaded = true
// this.WarmUpCache()
// }
ch := make(chan interface{})
go func() {
// Error handling
defer func() {
if r := recover(); r != nil {
ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
close(ch)
}
}()
cacheKey := fmt.Sprintf("%s", name)
cachedMethod, found := methodCache.Load(cacheKey)
if !found {
panic(name + " :method not found")
}
cachedMap := cachedMethod.(map[string]interface{})
// var method reflect.Method
// for i := 0; i < baseType.NumMethod(); i++ {
// method = baseType.Method(i)
// if name == method.Name {
// break
// }
// }
// method := cachedMap["method"].(reflect.Method)
methodValue := cachedMap["methodValue"].(reflect.Value)
methodType := cachedMap["methodType"].(reflect.Type)
numIn := cachedMap["numIn"].(int)
isVariadic := cachedMap["isVariadic"].(bool)
var in []reflect.Value
// Fixed argument handling for both regular and variadic functions
for k := 0; k < numIn-1; k++ {
if k < len(args) {
if args[k] == nil {
paramType := methodType.In(k + 1) // Account for receiver not being part of args
in = append(in, reflect.Zero(paramType))
} else {
in = append(in, reflect.ValueOf(args[k]))
}
} else {
paramType := methodType.In(k + 1) // Account for receiver not being part of args
in = append(in, reflect.Zero(paramType))
}
}
if isVariadic && len(args) >= numIn-1 {
variadicType := methodType.In(numIn - 1).Elem() // Get the type of the variadic argument
for k := numIn - 1; k < len(args); k++ {
if args[k] == nil {
in = append(in, reflect.Zero(variadicType))
} else {
in = append(in, reflect.ValueOf(args[k]))
}
}
}
// Call the method with the constructed arguments
res := methodValue.Call(in)
// Handle the result
if len(res) > 0 && res[0].Kind() == reflect.Chan {
resultChan := res[0]
go func() {
for {
val, ok := resultChan.Recv()
if !ok {
break // result channel is closed
}
ch <- val.Interface() // pass the value to the output channel
}
close(ch) // close the output channel after all values are received
}()
return
} else if len(res) > 0 {
ch <- res[0].Interface()
} else {
ch <- nil
}
ch <- nil
close(ch)
}()
return ch
}
// func CallInternalMethod(itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
// name := Capitalize(name2)
// baseValue := reflect.ValueOf(itf)
// baseType := baseValue.Type()
// ch := make(chan interface{})
// go func() {
// // Error handling
// defer func() {
// if r := recover(); r != nil {
// ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
// close(ch)
// }
// }()
// for i := 0; i < baseType.NumMethod(); i++ {
// method := baseType.Method(i)
// if name == method.Name {
// methodValue := baseValue.MethodByName(name)
// methodType := method.Type
// numIn := methodType.NumIn()
// isVariadic := methodType.IsVariadic()
// var in []reflect.Value
// // Fixed argument handling for both regular and variadic functions
// for k := 0; k < numIn-1; k++ {
// if k < len(args) {
// if args[k] == nil {
// paramType := methodType.In(k)
// in = append(in, reflect.Zero(paramType))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// } else {
// paramType := methodType.In(k)
// in = append(in, reflect.Zero(paramType))
// }
// }
// // Handle the variadic arguments
// if isVariadic && len(args) >= numIn-1 {
// variadicType := methodType.In(numIn - 1).Elem() // Get the type of the variadic argument
// for k := numIn - 1; k < len(args); k++ {
// if args[k] == nil {
// in = append(in, reflect.Zero(variadicType))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// }
// } else if len(args) >= numIn-1 {
// // Handle non-variadic arguments beyond fixed ones
// for k := numIn - 1; k < len(args); k++ {
// if args[k] == nil {
// paramType := methodType.In(k)
// in = append(in, reflect.Zero(paramType))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// }
// }
// // Call the method with the constructed arguments
// res := methodValue.Call(in)
// // Handle the result
// if len(res) > 0 && res[0].Kind() == reflect.Chan {
// resultChan := res[0]
// go func() {
// for {
// val, ok := resultChan.Recv()
// if !ok {
// break // result channel is closed
// }
// ch <- val.Interface() // pass the value to the output channel
// }
// close(ch) // close the output channel after all values are received
// }()
// return
// } else if len(res) > 0 {
// ch <- res[0].Interface()
// } else {
// ch <- nil
// }
// close(ch)
// return
// }
// }
// // If no method is found, return nil
// ch <- nil
// close(ch)
// }()
// return ch
// }
// original version not working for createExpiredEtc..
// func CallInternalMethod(itf interface{}, name2 string, args ...interface{}) <-chan interface{} {
// name := Capitalize(name2)
// baseType := reflect.TypeOf(itf)
// ch := make(chan interface{})
// go func() {
// // error handling
// defer func() {
// if r := recover(); r != nil {
// // panic(r)
// ch <- fmt.Sprintf("panic:%v:%v:%v", getCallerName(), name2, r)
// }
// }()
// for i := 0; i < baseType.NumMethod(); i++ {
// method := baseType.Method(i)
// if name == method.Name {
// methodType := method.Type
// numIn := methodType.NumIn()
// isVariadic := methodType.IsVariadic()
// var in []reflect.Value
// if isVariadic {
// // Handle fixed arguments
// for k := 0; k < numIn-1; k++ {
// if k < len(args) {
// if args[k] == nil {
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// } else {
// // paramType := methodType.In(k)
// // in = append(in, reflect.Zero(paramType))
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// }
// }
// // Handle variadic arguments
// // variadicType := methodType.In(numIn - 1).Elem()
// for k := numIn - 1; k < len(args); k++ {
// if args[k] == nil {
// in = append(in, reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// }
// } else {
// for k := 0; k < numIn; k++ {
// if k < len(args) {
// if args[k] == nil {
// paramType := methodType.In(k)
// in = append(in, reflect.Zero(paramType))
// } else {
// in = append(in, reflect.ValueOf(args[k]))
// }
// } else {
// paramType := methodType.In(k)
// in = append(in, reflect.Zero(paramType))
// }
// }
// }
// // Call the method
// res := reflect.ValueOf(itf).MethodByName(name).Call(in)
// // Check if the result is a channel
// if len(res) > 0 && res[0].Kind() == reflect.Chan {
// resultChan := res[0]
// // Read values from the returned channel and pass them to ch
// go func() {
// for {
// val, ok := resultChan.Recv()
// if !ok {
// break // result channel is closed
// }
// valInt := val.Interface()
// ch <- valInt // pass the value to the output channel
// }
// close(ch) // close the output channel after all values are received
// }()
// // // Don't close `ch` yet, as it will be closed after the resultChan is read
// return
// } else if len(res) > 0 {
// // Directly return the first result if it's not a channel
// val := res[0].Interface()
// ch <- val
// } else {
// // Return nil if no results
// ch <- nil
// }
// close(ch)
// return
// }
// }
// // If no method is found, return nil
// ch <- nil
// close(ch)
// }()
// return ch
// }
func PanicOnError(msg interface{}) {
caller := getCallerName()
switch v := msg.(type) {
case string:
if strings.HasPrefix(v, "panic:") {
panic(fmt.Sprintf("panic:%v:%v", caller, msg))
// panic(v)
}
case []interface{}:
for _, item := range v {
if str, ok := item.(string); ok && strings.HasPrefix(str, "panic:") {
// panic(fmt.Sprintf("panic:%v:%v", caller, str))
panic(str)
} else if nestedSlice, ok := item.([]interface{}); ok {
// Handle nested []interface{} cases recursively
PanicOnError(nestedSlice)
}
}
default:
return
}
}
func ReturnPanicError(ch chan interface{}) {
// https://stackoverflow.com/questions/72651899/why-golang-can-not-recover-from-a-panic-in-a-function-called-by-the-defer-functi
if r := recover(); r != nil {
if r != "break" {
strErr := ToString(r)
if !strings.HasPrefix(strErr, "panic:") {
ch <- "panic:" + strErr
} else {
ch <- strErr
}
}
}
}
func GetCallerName() string {
return getCallerName()
}
func getCallerName() string {
// Skip 2 levels to get the name of the caller of the function that called this one
pc, _, _, ok := runtime.Caller(3)
if !ok {
return "Unknown"
}
fn := runtime.FuncForPC(pc)
if fn == nil {
return "Unknown"
}
return fn.Name()
}
func Print(v interface{}) {
fmt.Println(v)
}