github.com/whatlly/hugo@v0.47.1/tpl/compare/compare.go (about) 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 15 16 import ( 17 "fmt" 18 "reflect" 19 "strconv" 20 "time" 21 22 "github.com/gohugoio/hugo/compare" 23 ) 24 25 // New returns a new instance of the compare-namespaced template functions. 26 func New() *Namespace { 27 return &Namespace{} 28 } 29 30 // Namespace provides template functions for the "compare" namespace. 31 type Namespace struct { 32 } 33 34 // Default checks whether a given value is set and returns a default value if it 35 // is not. "Set" in this context means non-zero for numeric types and times; 36 // non-zero length for strings, arrays, slices, and maps; 37 // any boolean or struct value; or non-nil for any other types. 38 func (*Namespace) Default(dflt interface{}, given ...interface{}) (interface{}, error) { 39 // given is variadic because the following construct will not pass a piped 40 // argument when the key is missing: {{ index . "key" | default "foo" }} 41 // The Go template will complain that we got 1 argument when we expectd 2. 42 43 if len(given) == 0 { 44 return dflt, nil 45 } 46 if len(given) != 1 { 47 return nil, fmt.Errorf("wrong number of args for default: want 2 got %d", len(given)+1) 48 } 49 50 g := reflect.ValueOf(given[0]) 51 if !g.IsValid() { 52 return dflt, nil 53 } 54 55 set := false 56 57 switch g.Kind() { 58 case reflect.Bool: 59 set = true 60 case reflect.String, reflect.Array, reflect.Slice, reflect.Map: 61 set = g.Len() != 0 62 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 63 set = g.Int() != 0 64 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 65 set = g.Uint() != 0 66 case reflect.Float32, reflect.Float64: 67 set = g.Float() != 0 68 case reflect.Complex64, reflect.Complex128: 69 set = g.Complex() != 0 70 case reflect.Struct: 71 switch actual := given[0].(type) { 72 case time.Time: 73 set = !actual.IsZero() 74 default: 75 set = true 76 } 77 default: 78 set = !g.IsNil() 79 } 80 81 if set { 82 return given[0], nil 83 } 84 85 return dflt, nil 86 } 87 88 // Eq returns the boolean truth of arg1 == arg2. 89 func (*Namespace) Eq(x, y interface{}) bool { 90 91 if e, ok := x.(compare.Eqer); ok { 92 return e.Eq(y) 93 } 94 95 if e, ok := y.(compare.Eqer); ok { 96 return e.Eq(x) 97 } 98 99 normalize := func(v interface{}) interface{} { 100 vv := reflect.ValueOf(v) 101 switch vv.Kind() { 102 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 103 return vv.Int() 104 case reflect.Float32, reflect.Float64: 105 return vv.Float() 106 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 107 return vv.Uint() 108 default: 109 return v 110 } 111 } 112 x = normalize(x) 113 y = normalize(y) 114 return reflect.DeepEqual(x, y) 115 } 116 117 // Ne returns the boolean truth of arg1 != arg2. 118 func (n *Namespace) Ne(x, y interface{}) bool { 119 return !n.Eq(x, y) 120 } 121 122 // Ge returns the boolean truth of arg1 >= arg2. 123 func (n *Namespace) Ge(a, b interface{}) bool { 124 left, right := n.compareGet(a, b) 125 return left >= right 126 } 127 128 // Gt returns the boolean truth of arg1 > arg2. 129 func (n *Namespace) Gt(a, b interface{}) bool { 130 left, right := n.compareGet(a, b) 131 return left > right 132 } 133 134 // Le returns the boolean truth of arg1 <= arg2. 135 func (n *Namespace) Le(a, b interface{}) bool { 136 left, right := n.compareGet(a, b) 137 return left <= right 138 } 139 140 // Lt returns the boolean truth of arg1 < arg2. 141 func (n *Namespace) Lt(a, b interface{}) bool { 142 left, right := n.compareGet(a, b) 143 return left < right 144 } 145 146 // Conditional can be used as a ternary operator. 147 // It returns a if condition, else b. 148 func (n *Namespace) Conditional(condition bool, a, b interface{}) interface{} { 149 if condition { 150 return a 151 } 152 return b 153 } 154 155 func (*Namespace) compareGet(a interface{}, b interface{}) (float64, float64) { 156 if ac, ok := a.(compare.Comparer); ok { 157 c := ac.Compare(b) 158 if c < 0 { 159 return 1, 0 160 } else if c == 0 { 161 return 0, 0 162 } else { 163 return 0, 1 164 } 165 } 166 167 if bc, ok := b.(compare.Comparer); ok { 168 c := bc.Compare(a) 169 if c < 0 { 170 return 0, 1 171 } else if c == 0 { 172 return 0, 0 173 } else { 174 return 1, 0 175 } 176 } 177 178 var left, right float64 179 var leftStr, rightStr *string 180 av := reflect.ValueOf(a) 181 182 switch av.Kind() { 183 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 184 left = float64(av.Len()) 185 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 186 left = float64(av.Int()) 187 case reflect.Float32, reflect.Float64: 188 left = av.Float() 189 case reflect.String: 190 var err error 191 left, err = strconv.ParseFloat(av.String(), 64) 192 if err != nil { 193 str := av.String() 194 leftStr = &str 195 } 196 case reflect.Struct: 197 switch av.Type() { 198 case timeType: 199 left = float64(toTimeUnix(av)) 200 } 201 } 202 203 bv := reflect.ValueOf(b) 204 205 switch bv.Kind() { 206 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 207 right = float64(bv.Len()) 208 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 209 right = float64(bv.Int()) 210 case reflect.Float32, reflect.Float64: 211 right = bv.Float() 212 case reflect.String: 213 var err error 214 right, err = strconv.ParseFloat(bv.String(), 64) 215 if err != nil { 216 str := bv.String() 217 rightStr = &str 218 } 219 case reflect.Struct: 220 switch bv.Type() { 221 case timeType: 222 right = float64(toTimeUnix(bv)) 223 } 224 } 225 226 switch { 227 case leftStr == nil || rightStr == nil: 228 case *leftStr < *rightStr: 229 return 0, 1 230 case *leftStr > *rightStr: 231 return 1, 0 232 default: 233 return 0, 0 234 } 235 236 return left, right 237 } 238 239 var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() 240 241 func toTimeUnix(v reflect.Value) int64 { 242 if v.Kind() == reflect.Interface { 243 return toTimeUnix(v.Elem()) 244 } 245 if v.Type() != timeType { 246 panic("coding error: argument must be time.Time type reflect Value") 247 } 248 return v.MethodByName("Unix").Call([]reflect.Value{})[0].Int() 249 }