github.com/openimsdk/tools@v0.0.49/mw/replace_nil.go (about)

     1  package mw
     2  
     3  import (
     4  	"github.com/openimsdk/tools/utils/datautil"
     5  	"reflect"
     6  )
     7  
     8  // ReplaceNil initialization nil values.
     9  // e.g. Slice will be initialized as [],Map/interface will be initialized as {}
    10  func ReplaceNil(data any) {
    11  	v := reflect.ValueOf(data)
    12  
    13  	replaceNil(v)
    14  }
    15  
    16  func replaceNil(v reflect.Value) {
    17  	switch v.Kind() {
    18  	case reflect.Pointer:
    19  		if v.IsNil() {
    20  			// Handle multi-level pointers
    21  			elemKind := v.Type().Elem().Kind()
    22  			if elemKind == reflect.Pointer {
    23  				v.Set(reflect.New(v.Type().Elem()))
    24  			}
    25  		}
    26  		replaceNil(v.Elem())
    27  	case reflect.Slice:
    28  		if v.IsNil() {
    29  			v.Set(reflect.MakeSlice(v.Type(), 0, 0))
    30  		}
    31  	case reflect.Map:
    32  		if v.IsNil() {
    33  			v.Set(reflect.MakeMap(v.Type()))
    34  		}
    35  	case reflect.Struct:
    36  		for i := 0; i < v.NumField(); i++ {
    37  			field := v.Field(i)
    38  			fieldType := v.Type().Field(i)
    39  
    40  			// Check if the field is exported
    41  			if fieldType.IsExported() {
    42  				replaceNil(field)
    43  			}
    44  		}
    45  	case reflect.Interface:
    46  		if !v.IsNil() && !shouldReplace(v) {
    47  			// If the interface is already initialized, recursively replace the internal nils
    48  			replaceNil(v.Elem())
    49  		} else {
    50  			// If the interface is not initialized, the struct will be initialized as {}
    51  			realType := getRealType(v.Interface())
    52  			if realType == nil {
    53  				// Invalid type
    54  				return
    55  			}
    56  			switch realType.Kind() {
    57  			case reflect.Slice:
    58  				v.Set(reflect.MakeSlice(realType, 0, 0))
    59  			case reflect.Map:
    60  				v.Set(reflect.MakeMap(realType))
    61  			case reflect.Struct:
    62  				v.Set(reflect.New(reflect.TypeOf(struct{}{})))
    63  			default:
    64  			}
    65  		}
    66  	default:
    67  		return
    68  	}
    69  }
    70  
    71  // getRealType determines the underlying type.
    72  func getRealType(data any) reflect.Type {
    73  	t := reflect.TypeOf(data)
    74  	if t == nil {
    75  		return t
    76  	}
    77  	for t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
    78  		t = t.Elem()
    79  	}
    80  	return t
    81  }
    82  
    83  // shouldReplace determines whether internal replacement is needed,
    84  // checks if the underlying component has already been initialized.
    85  func shouldReplace(v reflect.Value) bool {
    86  	if !v.IsValid() {
    87  		return true
    88  	}
    89  	if datautil.Contain(v.Kind(), []reflect.Kind{reflect.Slice, reflect.Map}...) && v.IsNil() {
    90  		return true
    91  	}
    92  	switch v.Kind() {
    93  	case reflect.Ptr:
    94  		return shouldReplace(v.Elem())
    95  	case reflect.Interface:
    96  		return shouldReplace(v.Elem())
    97  	default:
    98  		return false
    99  	}
   100  }