URI: 
       compare.go - hugo - [fork] hugo port for 9front
  HTML git clone https://git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       compare.go (9986B)
       ---
            1 // Copyright 2017 The Hugo Authors. All rights reserved.
            2 //
            3 // Licensed under the Apache License, Version 2.0 (the "License");
            4 // you may not use this file except in compliance with the License.
            5 // You may obtain a copy of the License at
            6 // http://www.apache.org/licenses/LICENSE-2.0
            7 //
            8 // Unless required by applicable law or agreed to in writing, software
            9 // distributed under the License is distributed on an "AS IS" BASIS,
           10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
           11 // See the License for the specific language governing permissions and
           12 // limitations under the License.
           13 
           14 // Package compare provides template functions for comparing values.
           15 package compare
           16 
           17 import (
           18         "fmt"
           19         "math"
           20         "reflect"
           21         "strconv"
           22         "time"
           23 
           24         "github.com/gohugoio/hugo/compare"
           25         "github.com/gohugoio/hugo/langs"
           26 
           27         "github.com/gohugoio/hugo/common/hreflect"
           28         "github.com/gohugoio/hugo/common/htime"
           29         "github.com/gohugoio/hugo/common/types"
           30 )
           31 
           32 // New returns a new instance of the compare-namespaced template functions.
           33 func New(loc *time.Location, caseInsensitive bool) *Namespace {
           34         return &Namespace{loc: loc, caseInsensitive: caseInsensitive}
           35 }
           36 
           37 // Namespace provides template functions for the "compare" namespace.
           38 type Namespace struct {
           39         loc *time.Location
           40         // Enable to do case insensitive string compares.
           41         caseInsensitive bool
           42 }
           43 
           44 // Default checks whether a givenv is set and returns the default value defaultv if it
           45 // is not.  "Set" in this context means non-zero for numeric types and times;
           46 // non-zero length for strings, arrays, slices, and maps;
           47 // any boolean or struct value; or non-nil for any other types.
           48 func (*Namespace) Default(defaultv any, givenv ...any) (any, error) {
           49         // given is variadic because the following construct will not pass a piped
           50         // argument when the key is missing:  {{ index . "key" | default "foo" }}
           51         // The Go template will complain that we got 1 argument when we expected 2.
           52 
           53         if len(givenv) == 0 {
           54                 return defaultv, nil
           55         }
           56         if len(givenv) != 1 {
           57                 return nil, fmt.Errorf("wrong number of args for default: want 2 got %d", len(givenv)+1)
           58         }
           59 
           60         g := reflect.ValueOf(givenv[0])
           61         if !g.IsValid() {
           62                 return defaultv, nil
           63         }
           64 
           65         set := false
           66 
           67         switch g.Kind() {
           68         case reflect.Bool:
           69                 set = true
           70         case reflect.String, reflect.Array, reflect.Slice, reflect.Map:
           71                 set = g.Len() != 0
           72         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
           73                 set = g.Int() != 0
           74         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
           75                 set = g.Uint() != 0
           76         case reflect.Float32, reflect.Float64:
           77                 set = g.Float() != 0
           78         case reflect.Complex64, reflect.Complex128:
           79                 set = g.Complex() != 0
           80         case reflect.Struct:
           81                 switch actual := givenv[0].(type) {
           82                 case time.Time:
           83                         set = !actual.IsZero()
           84                 default:
           85                         set = true
           86                 }
           87         default:
           88                 set = !g.IsNil()
           89         }
           90 
           91         if set {
           92                 return givenv[0], nil
           93         }
           94 
           95         return defaultv, nil
           96 }
           97 
           98 // Eq returns the boolean truth of arg1 == arg2 || arg1 == arg3 || arg1 == arg4.
           99 func (n *Namespace) Eq(first any, others ...any) bool {
          100         if n.caseInsensitive {
          101                 panic("caseInsensitive not implemented for Eq")
          102         }
          103         n.checkComparisonArgCount(1, others...)
          104         normalize := func(v any) any {
          105                 if types.IsNil(v) {
          106                         return nil
          107                 }
          108 
          109                 if at, ok := v.(htime.AsTimeProvider); ok {
          110                         return at.AsTime(n.loc)
          111                 }
          112 
          113                 vv := reflect.ValueOf(v)
          114                 switch vv.Kind() {
          115                 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
          116                         return vv.Int()
          117                 case reflect.Float32, reflect.Float64:
          118                         return vv.Float()
          119                 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
          120                         i := vv.Uint()
          121                         // If it can fit in an int, convert it.
          122                         if i <= math.MaxInt64 {
          123                                 return int64(i)
          124                         }
          125                         return i
          126                 case reflect.String:
          127                         return vv.String()
          128                 default:
          129                         return v
          130                 }
          131         }
          132 
          133         normFirst := normalize(first)
          134         for _, other := range others {
          135                 if e, ok := first.(compare.Eqer); ok {
          136                         if e.Eq(other) {
          137                                 return true
          138                         }
          139                         continue
          140                 }
          141 
          142                 if e, ok := other.(compare.Eqer); ok {
          143                         if e.Eq(first) {
          144                                 return true
          145                         }
          146                         continue
          147                 }
          148 
          149                 other = normalize(other)
          150                 if reflect.DeepEqual(normFirst, other) {
          151                         return true
          152                 }
          153         }
          154 
          155         return false
          156 }
          157 
          158 // Ne returns the boolean truth of arg1 != arg2 && arg1 != arg3 && arg1 != arg4.
          159 func (n *Namespace) Ne(first any, others ...any) bool {
          160         n.checkComparisonArgCount(1, others...)
          161         for _, other := range others {
          162                 if n.Eq(first, other) {
          163                         return false
          164                 }
          165         }
          166         return true
          167 }
          168 
          169 // Ge returns the boolean truth of arg1 >= arg2 && arg1 >= arg3 && arg1 >= arg4.
          170 func (n *Namespace) Ge(first any, others ...any) bool {
          171         n.checkComparisonArgCount(1, others...)
          172         for _, other := range others {
          173                 left, right := n.compareGet(first, other)
          174                 if !(left >= right) {
          175                         return false
          176                 }
          177         }
          178         return true
          179 }
          180 
          181 // Gt returns the boolean truth of arg1 > arg2 && arg1 > arg3 && arg1 > arg4.
          182 func (n *Namespace) Gt(first any, others ...any) bool {
          183         n.checkComparisonArgCount(1, others...)
          184         for _, other := range others {
          185                 left, right := n.compareGet(first, other)
          186                 if !(left > right) {
          187                         return false
          188                 }
          189         }
          190         return true
          191 }
          192 
          193 // Le returns the boolean truth of arg1 <= arg2 && arg1 <= arg3 && arg1 <= arg4.
          194 func (n *Namespace) Le(first any, others ...any) bool {
          195         n.checkComparisonArgCount(1, others...)
          196         for _, other := range others {
          197                 left, right := n.compareGet(first, other)
          198                 if !(left <= right) {
          199                         return false
          200                 }
          201         }
          202         return true
          203 }
          204 
          205 // LtCollate returns the boolean truth of arg1 < arg2 && arg1 < arg3 && arg1 < arg4.
          206 // The provided collator will be used for string comparisons.
          207 // This is for internal use.
          208 func (n *Namespace) LtCollate(collator *langs.Collator, first any, others ...any) bool {
          209         n.checkComparisonArgCount(1, others...)
          210         for _, other := range others {
          211                 left, right := n.compareGetWithCollator(collator, first, other)
          212                 if !(left < right) {
          213                         return false
          214                 }
          215         }
          216         return true
          217 }
          218 
          219 // Lt returns the boolean truth of arg1 < arg2 && arg1 < arg3 && arg1 < arg4.
          220 func (n *Namespace) Lt(first any, others ...any) bool {
          221         return n.LtCollate(nil, first, others...)
          222 }
          223 
          224 func (n *Namespace) checkComparisonArgCount(min int, others ...any) bool {
          225         if len(others) < min {
          226                 panic("missing arguments for comparison")
          227         }
          228         return true
          229 }
          230 
          231 // Conditional can be used as a ternary operator.
          232 //
          233 // It returns v1 if cond is true, else v2.
          234 func (n *Namespace) Conditional(cond any, v1, v2 any) any {
          235         if hreflect.IsTruthful(cond) {
          236                 return v1
          237         }
          238         return v2
          239 }
          240 
          241 func (ns *Namespace) compareGet(a any, b any) (float64, float64) {
          242         return ns.compareGetWithCollator(nil, a, b)
          243 }
          244 
          245 func (ns *Namespace) compareTwoUints(a uint64, b uint64) (float64, float64) {
          246         if a < b {
          247                 return 1, 0
          248         } else if a == b {
          249                 return 0, 0
          250         } else {
          251                 return 0, 1
          252         }
          253 }
          254 
          255 func (ns *Namespace) compareGetWithCollator(collator *langs.Collator, a any, b any) (float64, float64) {
          256         if ac, ok := a.(compare.Comparer); ok {
          257                 c := ac.Compare(b)
          258                 if c < 0 {
          259                         return 1, 0
          260                 } else if c == 0 {
          261                         return 0, 0
          262                 } else {
          263                         return 0, 1
          264                 }
          265         }
          266 
          267         if bc, ok := b.(compare.Comparer); ok {
          268                 c := bc.Compare(a)
          269                 if c < 0 {
          270                         return 0, 1
          271                 } else if c == 0 {
          272                         return 0, 0
          273                 } else {
          274                         return 1, 0
          275                 }
          276         }
          277 
          278         var left, right float64
          279         var leftStr, rightStr *string
          280         av := reflect.ValueOf(a)
          281         bv := reflect.ValueOf(b)
          282 
          283         switch av.Kind() {
          284         case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
          285                 left = float64(av.Len())
          286         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
          287                 if hreflect.IsUint(bv.Kind()) {
          288                         return ns.compareTwoUints(uint64(av.Int()), bv.Uint())
          289                 }
          290                 left = float64(av.Int())
          291         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
          292                 left = float64(av.Uint())
          293         case reflect.Uint64:
          294                 if hreflect.IsUint(bv.Kind()) {
          295                         return ns.compareTwoUints(av.Uint(), bv.Uint())
          296                 }
          297         case reflect.Float32, reflect.Float64:
          298                 left = av.Float()
          299         case reflect.String:
          300                 var err error
          301                 left, err = strconv.ParseFloat(av.String(), 64)
          302                 // Check if float is a special floating value and cast value as string.
          303                 if math.IsInf(left, 0) || math.IsNaN(left) || err != nil {
          304                         str := av.String()
          305                         leftStr = &str
          306                 }
          307         case reflect.Struct:
          308                 if hreflect.IsTime(av.Type()) {
          309                         left = float64(ns.toTimeUnix(av))
          310                 }
          311         case reflect.Bool:
          312                 left = 0
          313                 if av.Bool() {
          314                         left = 1
          315                 }
          316         }
          317 
          318         switch bv.Kind() {
          319         case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
          320                 right = float64(bv.Len())
          321         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
          322                 if hreflect.IsUint(av.Kind()) {
          323                         return ns.compareTwoUints(av.Uint(), uint64(bv.Int()))
          324                 }
          325                 right = float64(bv.Int())
          326         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
          327                 right = float64(bv.Uint())
          328         case reflect.Uint64:
          329                 if hreflect.IsUint(av.Kind()) {
          330                         return ns.compareTwoUints(av.Uint(), bv.Uint())
          331                 }
          332         case reflect.Float32, reflect.Float64:
          333                 right = bv.Float()
          334         case reflect.String:
          335                 var err error
          336                 right, err = strconv.ParseFloat(bv.String(), 64)
          337                 // Check if float is a special floating value and cast value as string.
          338                 if math.IsInf(right, 0) || math.IsNaN(right) || err != nil {
          339                         str := bv.String()
          340                         rightStr = &str
          341                 }
          342         case reflect.Struct:
          343                 if hreflect.IsTime(bv.Type()) {
          344                         right = float64(ns.toTimeUnix(bv))
          345                 }
          346         case reflect.Bool:
          347                 right = 0
          348                 if bv.Bool() {
          349                         right = 1
          350                 }
          351         }
          352 
          353         if (ns.caseInsensitive || collator != nil) && leftStr != nil && rightStr != nil {
          354                 var c int
          355                 if collator != nil {
          356                         c = collator.CompareStrings(*leftStr, *rightStr)
          357                 } else {
          358                         c = compare.Strings(*leftStr, *rightStr)
          359                 }
          360                 if c < 0 {
          361                         return 0, 1
          362                 } else if c > 0 {
          363                         return 1, 0
          364                 } else {
          365                         return 0, 0
          366                 }
          367         }
          368 
          369         switch {
          370         case leftStr == nil || rightStr == nil:
          371         case *leftStr < *rightStr:
          372                 return 0, 1
          373         case *leftStr > *rightStr:
          374                 return 1, 0
          375         default:
          376                 return 0, 0
          377         }
          378 
          379         return left, right
          380 }
          381 
          382 func (ns *Namespace) toTimeUnix(v reflect.Value) int64 {
          383         t, ok := hreflect.AsTime(v, ns.loc)
          384         if !ok {
          385                 panic("coding error: argument must be time.Time type reflect Value")
          386         }
          387         return t.Unix()
          388 }