Some checks failed
CI / Lint (push) Successful in -27m59s
CI / Test (1.25) (push) Successful in -27m46s
CI / Test (1.24) (push) Failing after 59s
CI / Build (push) Successful in -28m14s
Integration Tests / Integration Tests (push) Failing after -28m16s
Release / Build and Release (push) Successful in 1m1s
feat(templ): ✨ added templ to command line that reads go template and outputs code Reviewed-on: #1 Co-authored-by: Hein <hein.puth@gmail.com> Co-committed-by: Hein <hein.puth@gmail.com>
327 lines
6.4 KiB
Go
327 lines
6.4 KiB
Go
package reflectutil
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// Deref dereferences pointers until it reaches a non-pointer value
|
|
// Returns the dereferenced value and true if successful, or the original value and false if nil
|
|
func Deref(v reflect.Value) (reflect.Value, bool) {
|
|
for v.Kind() == reflect.Ptr {
|
|
if v.IsNil() {
|
|
return v, false
|
|
}
|
|
v = v.Elem()
|
|
}
|
|
return v, true
|
|
}
|
|
|
|
// DerefInterface dereferences an interface{} until it reaches a non-pointer value
|
|
func DerefInterface(i interface{}) reflect.Value {
|
|
v := reflect.ValueOf(i)
|
|
v, _ = Deref(v)
|
|
return v
|
|
}
|
|
|
|
// GetFieldValue extracts a field value from a struct, map, or pointer
|
|
// Returns nil if the field doesn't exist or can't be accessed
|
|
func GetFieldValue(item interface{}, field string) interface{} {
|
|
v := reflect.ValueOf(item)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Struct:
|
|
fieldVal := v.FieldByName(field)
|
|
if fieldVal.IsValid() {
|
|
return fieldVal.Interface()
|
|
}
|
|
return nil
|
|
|
|
case reflect.Map:
|
|
keyVal := reflect.ValueOf(field)
|
|
mapVal := v.MapIndex(keyVal)
|
|
if mapVal.IsValid() {
|
|
return mapVal.Interface()
|
|
}
|
|
return nil
|
|
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// IsSliceOrArray checks if an interface{} is a slice or array
|
|
func IsSliceOrArray(i interface{}) bool {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return false
|
|
}
|
|
k := v.Kind()
|
|
return k == reflect.Slice || k == reflect.Array
|
|
}
|
|
|
|
// IsMap checks if an interface{} is a map
|
|
func IsMap(i interface{}) bool {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return v.Kind() == reflect.Map
|
|
}
|
|
|
|
// SliceLen returns the length of a slice/array, or 0 if not a slice/array
|
|
func SliceLen(i interface{}) int {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
return 0
|
|
}
|
|
return v.Len()
|
|
}
|
|
|
|
// MapLen returns the length of a map, or 0 if not a map
|
|
func MapLen(i interface{}) int {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
if v.Kind() != reflect.Map {
|
|
return 0
|
|
}
|
|
return v.Len()
|
|
}
|
|
|
|
// SliceToInterfaces converts a slice/array to []interface{}
|
|
// Returns empty slice if not a slice/array
|
|
func SliceToInterfaces(i interface{}) []interface{} {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return []interface{}{}
|
|
}
|
|
|
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
return []interface{}{}
|
|
}
|
|
|
|
result := make([]interface{}, v.Len())
|
|
for i := 0; i < v.Len(); i++ {
|
|
result[i] = v.Index(i).Interface()
|
|
}
|
|
return result
|
|
}
|
|
|
|
// MapKeys returns all keys from a map as []interface{}
|
|
// Returns empty slice if not a map
|
|
func MapKeys(i interface{}) []interface{} {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return []interface{}{}
|
|
}
|
|
|
|
if v.Kind() != reflect.Map {
|
|
return []interface{}{}
|
|
}
|
|
|
|
keys := v.MapKeys()
|
|
result := make([]interface{}, len(keys))
|
|
for i, key := range keys {
|
|
result[i] = key.Interface()
|
|
}
|
|
return result
|
|
}
|
|
|
|
// MapValues returns all values from a map as []interface{}
|
|
// Returns empty slice if not a map
|
|
func MapValues(i interface{}) []interface{} {
|
|
v := reflect.ValueOf(i)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return []interface{}{}
|
|
}
|
|
|
|
if v.Kind() != reflect.Map {
|
|
return []interface{}{}
|
|
}
|
|
|
|
result := make([]interface{}, 0, v.Len())
|
|
iter := v.MapRange()
|
|
for iter.Next() {
|
|
result = append(result, iter.Value().Interface())
|
|
}
|
|
return result
|
|
}
|
|
|
|
// MapGet safely gets a value from a map by key
|
|
// Returns nil if key doesn't exist or not a map
|
|
func MapGet(m interface{}, key interface{}) interface{} {
|
|
v := reflect.ValueOf(m)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
if v.Kind() != reflect.Map {
|
|
return nil
|
|
}
|
|
|
|
keyVal := reflect.ValueOf(key)
|
|
mapVal := v.MapIndex(keyVal)
|
|
if mapVal.IsValid() {
|
|
return mapVal.Interface()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SliceIndex safely gets an element from a slice/array by index
|
|
// Returns nil if index out of bounds or not a slice/array
|
|
func SliceIndex(slice interface{}, index int) interface{} {
|
|
v := reflect.ValueOf(slice)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
|
|
return nil
|
|
}
|
|
|
|
if index < 0 || index >= v.Len() {
|
|
return nil
|
|
}
|
|
|
|
return v.Index(index).Interface()
|
|
}
|
|
|
|
// CompareValues compares two values for sorting
|
|
// Returns -1 if a < b, 0 if a == b, 1 if a > b
|
|
func CompareValues(a, b interface{}) int {
|
|
if a == nil && b == nil {
|
|
return 0
|
|
}
|
|
if a == nil {
|
|
return -1
|
|
}
|
|
if b == nil {
|
|
return 1
|
|
}
|
|
|
|
va := reflect.ValueOf(a)
|
|
vb := reflect.ValueOf(b)
|
|
|
|
// Handle different types
|
|
switch va.Kind() {
|
|
case reflect.String:
|
|
if vb.Kind() == reflect.String {
|
|
as := va.String()
|
|
bs := vb.String()
|
|
if as < bs {
|
|
return -1
|
|
} else if as > bs {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
if vb.Kind() >= reflect.Int && vb.Kind() <= reflect.Int64 {
|
|
ai := va.Int()
|
|
bi := vb.Int()
|
|
if ai < bi {
|
|
return -1
|
|
} else if ai > bi {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
if vb.Kind() >= reflect.Uint && vb.Kind() <= reflect.Uint64 {
|
|
au := va.Uint()
|
|
bu := vb.Uint()
|
|
if au < bu {
|
|
return -1
|
|
} else if au > bu {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
if vb.Kind() == reflect.Float32 || vb.Kind() == reflect.Float64 {
|
|
af := va.Float()
|
|
bf := vb.Float()
|
|
if af < bf {
|
|
return -1
|
|
} else if af > bf {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// GetNestedValue gets a nested value using dot notation path
|
|
// Example: GetNestedValue(obj, "database.schema.table")
|
|
func GetNestedValue(m interface{}, path string) interface{} {
|
|
if path == "" {
|
|
return m
|
|
}
|
|
|
|
parts := strings.Split(path, ".")
|
|
current := m
|
|
|
|
for _, part := range parts {
|
|
if current == nil {
|
|
return nil
|
|
}
|
|
|
|
v := reflect.ValueOf(current)
|
|
v, ok := Deref(v)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
switch v.Kind() {
|
|
case reflect.Map:
|
|
keyVal := reflect.ValueOf(part)
|
|
mapVal := v.MapIndex(keyVal)
|
|
if !mapVal.IsValid() {
|
|
return nil
|
|
}
|
|
current = mapVal.Interface()
|
|
|
|
case reflect.Struct:
|
|
fieldVal := v.FieldByName(part)
|
|
if !fieldVal.IsValid() {
|
|
return nil
|
|
}
|
|
current = fieldVal.Interface()
|
|
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return current
|
|
}
|
|
|
|
// DeepEqual performs a deep equality check between two values
|
|
func DeepEqual(a, b interface{}) bool {
|
|
return reflect.DeepEqual(a, b)
|
|
}
|