github.com/gogf/gf/v2@v2.7.4/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/gogf/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/gogf/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  			rv = reflect.ValueOf(value)
   101  			if IsNil(rv) {
   102  				return true
   103  			}
   104  
   105  			// =========================
   106  			// Common interfaces checks.
   107  			// =========================
   108  			if f, ok := value.(iTime); ok {
   109  				if f == (*time.Time)(nil) {
   110  					return true
   111  				}
   112  				return f.IsZero()
   113  			}
   114  			if f, ok := value.(iString); ok {
   115  				if f == nil {
   116  					return true
   117  				}
   118  				return f.String() == ""
   119  			}
   120  			if f, ok := value.(iInterfaces); ok {
   121  				if f == nil {
   122  					return true
   123  				}
   124  				return len(f.Interfaces()) == 0
   125  			}
   126  			if f, ok := value.(iMapStrAny); ok {
   127  				if f == nil {
   128  					return true
   129  				}
   130  				return len(f.MapStrAny()) == 0
   131  			}
   132  		}
   133  
   134  		switch rv.Kind() {
   135  		case reflect.Bool:
   136  			return !rv.Bool()
   137  
   138  		case
   139  			reflect.Int,
   140  			reflect.Int8,
   141  			reflect.Int16,
   142  			reflect.Int32,
   143  			reflect.Int64:
   144  			return rv.Int() == 0
   145  
   146  		case
   147  			reflect.Uint,
   148  			reflect.Uint8,
   149  			reflect.Uint16,
   150  			reflect.Uint32,
   151  			reflect.Uint64,
   152  			reflect.Uintptr:
   153  			return rv.Uint() == 0
   154  
   155  		case
   156  			reflect.Float32,
   157  			reflect.Float64:
   158  			return rv.Float() == 0
   159  
   160  		case reflect.String:
   161  			return rv.Len() == 0
   162  
   163  		case reflect.Struct:
   164  			var fieldValueInterface interface{}
   165  			for i := 0; i < rv.NumField(); i++ {
   166  				fieldValueInterface, _ = reflection.ValueToInterface(rv.Field(i))
   167  				if !IsEmpty(fieldValueInterface) {
   168  					return false
   169  				}
   170  			}
   171  			return true
   172  
   173  		case
   174  			reflect.Chan,
   175  			reflect.Map,
   176  			reflect.Slice,
   177  			reflect.Array:
   178  			return rv.Len() == 0
   179  
   180  		case reflect.Ptr:
   181  			if len(traceSource) > 0 && traceSource[0] {
   182  				return IsEmpty(rv.Elem())
   183  			}
   184  			return rv.IsNil()
   185  
   186  		case
   187  			reflect.Func,
   188  			reflect.Interface,
   189  			reflect.UnsafePointer:
   190  			return rv.IsNil()
   191  
   192  		case reflect.Invalid:
   193  			return true
   194  
   195  		default:
   196  			return false
   197  		}
   198  	}
   199  }
   200  
   201  // IsNil checks whether given `value` is nil, especially for interface{} type value.
   202  // Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
   203  // that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
   204  // Note that it might use reflect feature which affects performance a little.
   205  func IsNil(value interface{}, traceSource ...bool) bool {
   206  	if value == nil {
   207  		return true
   208  	}
   209  	var rv reflect.Value
   210  	if v, ok := value.(reflect.Value); ok {
   211  		rv = v
   212  	} else {
   213  		rv = reflect.ValueOf(value)
   214  	}
   215  	switch rv.Kind() {
   216  	case reflect.Chan,
   217  		reflect.Map,
   218  		reflect.Slice,
   219  		reflect.Func,
   220  		reflect.Interface,
   221  		reflect.UnsafePointer:
   222  		return !rv.IsValid() || rv.IsNil()
   223  
   224  	case reflect.Ptr:
   225  		if len(traceSource) > 0 && traceSource[0] {
   226  			for rv.Kind() == reflect.Ptr {
   227  				rv = rv.Elem()
   228  			}
   229  			if !rv.IsValid() {
   230  				return true
   231  			}
   232  			if rv.Kind() == reflect.Ptr {
   233  				return rv.IsNil()
   234  			}
   235  		} else {
   236  			return !rv.IsValid() || rv.IsNil()
   237  		}
   238  
   239  	default:
   240  		return false
   241  	}
   242  	return false
   243  }