github.com/wangyougui/gf/v2@v2.6.5/internal/empty/empty.go (about)

     1  // Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  // Package empty provides functions for checking empty/nil variables.
     8  package empty
     9  
    10  import (
    11  	"reflect"
    12  	"time"
    13  
    14  	"github.com/wangyougui/gf/v2/internal/reflection"
    15  )
    16  
    17  // iString is used for type assert api for String().
    18  type iString interface {
    19  	String() string
    20  }
    21  
    22  // iInterfaces is used for type assert api for Interfaces.
    23  type iInterfaces interface {
    24  	Interfaces() []interface{}
    25  }
    26  
    27  // iMapStrAny is the interface support for converting struct parameter to map.
    28  type iMapStrAny interface {
    29  	MapStrAny() map[string]interface{}
    30  }
    31  
    32  type iTime interface {
    33  	Date() (year int, month time.Month, day int)
    34  	IsZero() bool
    35  }
    36  
    37  // IsEmpty checks whether given `value` empty.
    38  // It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0,
    39  // or else it returns false.
    40  //
    41  // The parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
    42  // that also points to a pointer. It returns true if the source is empty when `traceSource` is true.
    43  // Note that it might use reflect feature which affects performance a little.
    44  func IsEmpty(value interface{}, traceSource ...bool) bool {
    45  	if value == nil {
    46  		return true
    47  	}
    48  	// It firstly checks the variable as common types using assertion to enhance the performance,
    49  	// and then using reflection.
    50  	switch result := value.(type) {
    51  	case int:
    52  		return result == 0
    53  	case int8:
    54  		return result == 0
    55  	case int16:
    56  		return result == 0
    57  	case int32:
    58  		return result == 0
    59  	case int64:
    60  		return result == 0
    61  	case uint:
    62  		return result == 0
    63  	case uint8:
    64  		return result == 0
    65  	case uint16:
    66  		return result == 0
    67  	case uint32:
    68  		return result == 0
    69  	case uint64:
    70  		return result == 0
    71  	case float32:
    72  		return result == 0
    73  	case float64:
    74  		return result == 0
    75  	case bool:
    76  		return !result
    77  	case string:
    78  		return result == ""
    79  	case []byte:
    80  		return len(result) == 0
    81  	case []rune:
    82  		return len(result) == 0
    83  	case []int:
    84  		return len(result) == 0
    85  	case []string:
    86  		return len(result) == 0
    87  	case []float32:
    88  		return len(result) == 0
    89  	case []float64:
    90  		return len(result) == 0
    91  	case map[string]interface{}:
    92  		return len(result) == 0
    93  
    94  	default:
    95  		// Finally, using reflect.
    96  		var rv reflect.Value
    97  		if v, ok := value.(reflect.Value); ok {
    98  			rv = v
    99  		} else {
   100  			// =========================
   101  			// Common interfaces checks.
   102  			// =========================
   103  			if f, ok := value.(iTime); ok {
   104  				if f == (*time.Time)(nil) {
   105  					return true
   106  				}
   107  				return f.IsZero()
   108  			}
   109  			if f, ok := value.(iString); ok {
   110  				if f == nil {
   111  					return true
   112  				}
   113  				return f.String() == ""
   114  			}
   115  			if f, ok := value.(iInterfaces); ok {
   116  				if f == nil {
   117  					return true
   118  				}
   119  				return len(f.Interfaces()) == 0
   120  			}
   121  			if f, ok := value.(iMapStrAny); ok {
   122  				if f == nil {
   123  					return true
   124  				}
   125  				return len(f.MapStrAny()) == 0
   126  			}
   127  
   128  			rv = reflect.ValueOf(value)
   129  		}
   130  
   131  		switch rv.Kind() {
   132  		case reflect.Bool:
   133  			return !rv.Bool()
   134  
   135  		case
   136  			reflect.Int,
   137  			reflect.Int8,
   138  			reflect.Int16,
   139  			reflect.Int32,
   140  			reflect.Int64:
   141  			return rv.Int() == 0
   142  
   143  		case
   144  			reflect.Uint,
   145  			reflect.Uint8,
   146  			reflect.Uint16,
   147  			reflect.Uint32,
   148  			reflect.Uint64,
   149  			reflect.Uintptr:
   150  			return rv.Uint() == 0
   151  
   152  		case
   153  			reflect.Float32,
   154  			reflect.Float64:
   155  			return rv.Float() == 0
   156  
   157  		case reflect.String:
   158  			return rv.Len() == 0
   159  
   160  		case reflect.Struct:
   161  			var fieldValueInterface interface{}
   162  			for i := 0; i < rv.NumField(); i++ {
   163  				fieldValueInterface, _ = reflection.ValueToInterface(rv.Field(i))
   164  				if !IsEmpty(fieldValueInterface) {
   165  					return false
   166  				}
   167  			}
   168  			return true
   169  
   170  		case
   171  			reflect.Chan,
   172  			reflect.Map,
   173  			reflect.Slice,
   174  			reflect.Array:
   175  			return rv.Len() == 0
   176  
   177  		case reflect.Ptr:
   178  			if len(traceSource) > 0 && traceSource[0] {
   179  				return IsEmpty(rv.Elem())
   180  			}
   181  			return rv.IsNil()
   182  
   183  		case
   184  			reflect.Func,
   185  			reflect.Interface,
   186  			reflect.UnsafePointer:
   187  			return rv.IsNil()
   188  
   189  		case reflect.Invalid:
   190  			return true
   191  		}
   192  	}
   193  	return false
   194  }
   195  
   196  // IsNil checks whether given `value` is nil, especially for interface{} type value.
   197  // Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
   198  // that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
   199  // Note that it might use reflect feature which affects performance a little.
   200  func IsNil(value interface{}, traceSource ...bool) bool {
   201  	if value == nil {
   202  		return true
   203  	}
   204  	var rv reflect.Value
   205  	if v, ok := value.(reflect.Value); ok {
   206  		rv = v
   207  	} else {
   208  		rv = reflect.ValueOf(value)
   209  	}
   210  	switch rv.Kind() {
   211  	case reflect.Chan,
   212  		reflect.Map,
   213  		reflect.Slice,
   214  		reflect.Func,
   215  		reflect.Interface,
   216  		reflect.UnsafePointer:
   217  		return !rv.IsValid() || rv.IsNil()
   218  
   219  	case reflect.Ptr:
   220  		if len(traceSource) > 0 && traceSource[0] {
   221  			for rv.Kind() == reflect.Ptr {
   222  				rv = rv.Elem()
   223  			}
   224  			if !rv.IsValid() {
   225  				return true
   226  			}
   227  			if rv.Kind() == reflect.Ptr {
   228  				return rv.IsNil()
   229  			}
   230  		} else {
   231  			return !rv.IsValid() || rv.IsNil()
   232  		}
   233  	}
   234  	return false
   235  }