hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

helpers.go (5850B)

    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 	"context"
   21 	"reflect"
   22 	"sync"
   23 	"time"
   24 
   25 	"github.com/gohugoio/hugo/common/htime"
   26 	"github.com/gohugoio/hugo/common/types"
   27 )
   28 
   29 // TODO(bep) replace the private versions in /tpl with these.
   30 // IsNumber returns whether the given kind is a number.
   31 func IsNumber(kind reflect.Kind) bool {
   32 	return IsInt(kind) || IsUint(kind) || IsFloat(kind)
   33 }
   34 
   35 // IsInt returns whether the given kind is an int.
   36 func IsInt(kind reflect.Kind) bool {
   37 	switch kind {
   38 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   39 		return true
   40 	default:
   41 		return false
   42 	}
   43 }
   44 
   45 // IsUint returns whether the given kind is an uint.
   46 func IsUint(kind reflect.Kind) bool {
   47 	switch kind {
   48 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   49 		return true
   50 	default:
   51 		return false
   52 	}
   53 }
   54 
   55 // IsFloat returns whether the given kind is a float.
   56 func IsFloat(kind reflect.Kind) bool {
   57 	switch kind {
   58 	case reflect.Float32, reflect.Float64:
   59 		return true
   60 	default:
   61 		return false
   62 	}
   63 }
   64 
   65 // IsTruthful returns whether in represents a truthful value.
   66 // See IsTruthfulValue
   67 func IsTruthful(in any) bool {
   68 	switch v := in.(type) {
   69 	case reflect.Value:
   70 		return IsTruthfulValue(v)
   71 	default:
   72 		return IsTruthfulValue(reflect.ValueOf(in))
   73 	}
   74 }
   75 
   76 var zeroType = reflect.TypeOf((*types.Zeroer)(nil)).Elem()
   77 
   78 // IsTruthfulValue returns whether the given value has a meaningful truth value.
   79 // This is based on template.IsTrue in Go's stdlib, but also considers
   80 // IsZero and any interface value will be unwrapped before it's considered
   81 // for truthfulness.
   82 //
   83 // Based on:
   84 // https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L306
   85 func IsTruthfulValue(val reflect.Value) (truth bool) {
   86 	val = indirectInterface(val)
   87 
   88 	if !val.IsValid() {
   89 		// Something like var x interface{}, never set. It's a form of nil.
   90 		return
   91 	}
   92 
   93 	if val.Type().Implements(zeroType) {
   94 		return !val.Interface().(types.Zeroer).IsZero()
   95 	}
   96 
   97 	switch val.Kind() {
   98 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
   99 		truth = val.Len() > 0
  100 	case reflect.Bool:
  101 		truth = val.Bool()
  102 	case reflect.Complex64, reflect.Complex128:
  103 		truth = val.Complex() != 0
  104 	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Interface:
  105 		truth = !val.IsNil()
  106 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  107 		truth = val.Int() != 0
  108 	case reflect.Float32, reflect.Float64:
  109 		truth = val.Float() != 0
  110 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  111 		truth = val.Uint() != 0
  112 	case reflect.Struct:
  113 		truth = true // Struct values are always true.
  114 	default:
  115 		return
  116 	}
  117 
  118 	return
  119 }
  120 
  121 type methodKey struct {
  122 	typ  reflect.Type
  123 	name string
  124 }
  125 
  126 type methods struct {
  127 	sync.RWMutex
  128 	cache map[methodKey]int
  129 }
  130 
  131 var methodCache = &methods{cache: make(map[methodKey]int)}
  132 
  133 // GetMethodByName is the same as reflect.Value.MethodByName, but it caches the
  134 // type lookup.
  135 func GetMethodByName(v reflect.Value, name string) reflect.Value {
  136 	index := GetMethodIndexByName(v.Type(), name)
  137 
  138 	if index == -1 {
  139 		return reflect.Value{}
  140 	}
  141 
  142 	return v.Method(index)
  143 }
  144 
  145 // GetMethodIndexByName returns the index of the method with the given name, or
  146 // -1 if no such method exists.
  147 func GetMethodIndexByName(tp reflect.Type, name string) int {
  148 	k := methodKey{tp, name}
  149 	methodCache.RLock()
  150 	index, found := methodCache.cache[k]
  151 	methodCache.RUnlock()
  152 	if found {
  153 		return index
  154 	}
  155 
  156 	methodCache.Lock()
  157 	defer methodCache.Unlock()
  158 
  159 	m, ok := tp.MethodByName(name)
  160 	index = m.Index
  161 	if !ok {
  162 		index = -1
  163 	}
  164 	methodCache.cache[k] = index
  165 
  166 	if !ok {
  167 		return -1
  168 	}
  169 
  170 	return m.Index
  171 }
  172 
  173 var (
  174 	timeType           = reflect.TypeOf((*time.Time)(nil)).Elem()
  175 	asTimeProviderType = reflect.TypeOf((*htime.AsTimeProvider)(nil)).Elem()
  176 )
  177 
  178 // IsTime returns whether tp is a time.Time type or if it can be converted into one
  179 // in ToTime.
  180 func IsTime(tp reflect.Type) bool {
  181 	if tp == timeType {
  182 		return true
  183 	}
  184 
  185 	if tp.Implements(asTimeProviderType) {
  186 		return true
  187 	}
  188 	return false
  189 }
  190 
  191 // AsTime returns v as a time.Time if possible.
  192 // The given location is only used if the value implements AsTimeProvider (e.g. go-toml local).
  193 // A zero Time and false is returned if this isn't possible.
  194 // Note that this function does not accept string dates.
  195 func AsTime(v reflect.Value, loc *time.Location) (time.Time, bool) {
  196 	if v.Kind() == reflect.Interface {
  197 		return AsTime(v.Elem(), loc)
  198 	}
  199 
  200 	if v.Type() == timeType {
  201 		return v.Interface().(time.Time), true
  202 	}
  203 
  204 	if v.Type().Implements(asTimeProviderType) {
  205 		return v.Interface().(htime.AsTimeProvider).AsTime(loc), true
  206 	}
  207 
  208 	return time.Time{}, false
  209 }
  210 
  211 // Based on: https://github.com/golang/go/blob/178a2c42254166cffed1b25fb1d3c7a5727cada6/src/text/template/exec.go#L931
  212 func indirectInterface(v reflect.Value) reflect.Value {
  213 	if v.Kind() != reflect.Interface {
  214 		return v
  215 	}
  216 	if v.IsNil() {
  217 		return reflect.Value{}
  218 	}
  219 	return v.Elem()
  220 }
  221 
  222 var ContextInterface = reflect.TypeOf((*context.Context)(nil)).Elem()