github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/common/hreflect/helpers.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  // Some functions in this file (see comments) is based on the Go source code,
     3  // copyright The Go Authors and  governed by a BSD-style license.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  // http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  // Package hreflect contains reflect helpers.
    17  package hreflect
    18  
    19  import (
    20  	"reflect"
    21  
    22  	"github.com/gohugoio/hugo/common/types"
    23  )
    24  
    25  // TODO(bep) replace the private versions in /tpl with these.
    26  // IsNumber returns whether the given kind is a number.
    27  func IsNumber(kind reflect.Kind) bool {
    28  	return IsInt(kind) || IsUint(kind) || IsFloat(kind)
    29  }
    30  
    31  // IsInt returns whether the given kind is an int.
    32  func IsInt(kind reflect.Kind) bool {
    33  	switch kind {
    34  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    35  		return true
    36  	default:
    37  		return false
    38  	}
    39  }
    40  
    41  // IsUint returns whether the given kind is an uint.
    42  func IsUint(kind reflect.Kind) bool {
    43  	switch kind {
    44  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    45  		return true
    46  	default:
    47  		return false
    48  	}
    49  }
    50  
    51  // IsFloat returns whether the given kind is a float.
    52  func IsFloat(kind reflect.Kind) bool {
    53  	switch kind {
    54  	case reflect.Float32, reflect.Float64:
    55  		return true
    56  	default:
    57  		return false
    58  	}
    59  }
    60  
    61  // IsTruthful returns whether in represents a truthful value.
    62  // See IsTruthfulValue
    63  func IsTruthful(in interface{}) bool {
    64  	switch v := in.(type) {
    65  	case reflect.Value:
    66  		return IsTruthfulValue(v)
    67  	default:
    68  		return IsTruthfulValue(reflect.ValueOf(in))
    69  	}
    70  }
    71  
    72  var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem()
    73  
    74  // IsTruthfulValue returns whether the given value has a meaningful truth value.
    75  // This is based on template.IsTrue in Go's stdlib, but also considers
    76  // IsZero and any interface value will be unwrapped before it's considered
    77  // for truthfulness.
    78  //
    79  // Based on:
    80  // https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L306
    81  func IsTruthfulValue(val reflect.Value) (truth bool) {
    82  	val = indirectInterface(val)
    83  
    84  	if !val.IsValid() {
    85  		// Something like var x interface{}, never set. It's a form of nil.
    86  		return
    87  	}
    88  
    89  	if val.Type().Implements(zeroType) {
    90  		return !val.Interface().(types.Zeroer).IsZero()
    91  	}
    92  
    93  	switch val.Kind() {
    94  	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
    95  		truth = val.Len() > 0
    96  	case reflect.Bool:
    97  		truth = val.Bool()
    98  	case reflect.Complex64, reflect.Complex128:
    99  		truth = val.Complex() != 0
   100  	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
   101  		truth = !val.IsNil()
   102  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   103  		truth = val.Int() != 0
   104  	case reflect.Float32, reflect.Float64:
   105  		truth = val.Float() != 0
   106  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   107  		truth = val.Uint() != 0
   108  	case reflect.Struct:
   109  		truth = true // Struct values are always true.
   110  	default:
   111  		return
   112  	}
   113  
   114  	return
   115  }
   116  
   117  // Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
   118  func indirectInterface(v reflect.Value) reflect.Value {
   119  	if v.Kind() != reflect.Interface {
   120  		return v
   121  	}
   122  	if v.IsNil() {
   123  		return reflect.Value{}
   124  	}
   125  	return v.Elem()
   126  }