github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/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 provides template functions for comparing values. 15 package compare 16 17 import ( 18 "fmt" 19 "reflect" 20 "strconv" 21 "time" 22 23 "github.com/gohugoio/hugo/compare" 24 25 "github.com/gohugoio/hugo/common/types" 26 ) 27 28 // New returns a new instance of the compare-namespaced template functions. 29 func New(caseInsensitive bool) *Namespace { 30 return &Namespace{caseInsensitive: caseInsensitive} 31 } 32 33 // Namespace provides template functions for the "compare" namespace. 34 type Namespace struct { 35 // Enable to do case insensitive string compares. 36 caseInsensitive bool 37 } 38 39 // Default checks whether a given value is set and returns a default value if it 40 // is not. "Set" in this context means non-zero for numeric types and times; 41 // non-zero length for strings, arrays, slices, and maps; 42 // any boolean or struct value; or non-nil for any other types. 43 func (*Namespace) Default(dflt interface{}, given ...interface{}) (interface{}, error) { 44 // given is variadic because the following construct will not pass a piped 45 // argument when the key is missing: {{ index . "key" | default "foo" }} 46 // The Go template will complain that we got 1 argument when we expected 2. 47 48 if len(given) == 0 { 49 return dflt, nil 50 } 51 if len(given) != 1 { 52 return nil, fmt.Errorf("wrong number of args for default: want 2 got %d", len(given)+1) 53 } 54 55 g := reflect.ValueOf(given[0]) 56 if !g.IsValid() { 57 return dflt, nil 58 } 59 60 set := false 61 62 switch g.Kind() { 63 case reflect.Bool: 64 set = true 65 case reflect.String, reflect.Array, reflect.Slice, reflect.Map: 66 set = g.Len() != 0 67 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 68 set = g.Int() != 0 69 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 70 set = g.Uint() != 0 71 case reflect.Float32, reflect.Float64: 72 set = g.Float() != 0 73 case reflect.Complex64, reflect.Complex128: 74 set = g.Complex() != 0 75 case reflect.Struct: 76 switch actual := given[0].(type) { 77 case time.Time: 78 set = !actual.IsZero() 79 default: 80 set = true 81 } 82 default: 83 set = !g.IsNil() 84 } 85 86 if set { 87 return given[0], nil 88 } 89 90 return dflt, nil 91 } 92 93 // Eq returns the boolean truth of arg1 == arg2 || arg1 == arg3 || arg1 == arg4. 94 func (n *Namespace) Eq(first interface{}, others ...interface{}) bool { 95 if n.caseInsensitive { 96 panic("caseInsensitive not implemented for Eq") 97 } 98 n.checkComparisonArgCount(1, others...) 99 normalize := func(v interface{}) interface{} { 100 if types.IsNil(v) { 101 return nil 102 } 103 vv := reflect.ValueOf(v) 104 switch vv.Kind() { 105 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 106 return vv.Int() 107 case reflect.Float32, reflect.Float64: 108 return vv.Float() 109 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 110 return vv.Uint() 111 case reflect.String: 112 return vv.String() 113 default: 114 return v 115 } 116 } 117 118 normFirst := normalize(first) 119 for _, other := range others { 120 if e, ok := first.(compare.Eqer); ok { 121 if e.Eq(other) { 122 return true 123 } 124 continue 125 } 126 127 if e, ok := other.(compare.Eqer); ok { 128 if e.Eq(first) { 129 return true 130 } 131 continue 132 } 133 134 other = normalize(other) 135 if reflect.DeepEqual(normFirst, other) { 136 return true 137 } 138 } 139 140 return false 141 } 142 143 // Ne returns the boolean truth of arg1 != arg2 && arg1 != arg3 && arg1 != arg4. 144 func (n *Namespace) Ne(first interface{}, others ...interface{}) bool { 145 n.checkComparisonArgCount(1, others...) 146 for _, other := range others { 147 if n.Eq(first, other) { 148 return false 149 } 150 } 151 return true 152 } 153 154 // Ge returns the boolean truth of arg1 >= arg2 && arg1 >= arg3 && arg1 >= arg4. 155 func (n *Namespace) Ge(first interface{}, others ...interface{}) bool { 156 n.checkComparisonArgCount(1, others...) 157 for _, other := range others { 158 left, right := n.compareGet(first, other) 159 if !(left >= right) { 160 return false 161 } 162 } 163 return true 164 } 165 166 // Gt returns the boolean truth of arg1 > arg2 && arg1 > arg3 && arg1 > arg4. 167 func (n *Namespace) Gt(first interface{}, others ...interface{}) bool { 168 n.checkComparisonArgCount(1, others...) 169 for _, other := range others { 170 left, right := n.compareGet(first, other) 171 if !(left > right) { 172 return false 173 } 174 } 175 return true 176 } 177 178 // Le returns the boolean truth of arg1 <= arg2 && arg1 <= arg3 && arg1 <= arg4. 179 func (n *Namespace) Le(first interface{}, others ...interface{}) bool { 180 n.checkComparisonArgCount(1, others...) 181 for _, other := range others { 182 left, right := n.compareGet(first, other) 183 if !(left <= right) { 184 return false 185 } 186 } 187 return true 188 } 189 190 // Lt returns the boolean truth of arg1 < arg2 && arg1 < arg3 && arg1 < arg4. 191 func (n *Namespace) Lt(first interface{}, others ...interface{}) bool { 192 n.checkComparisonArgCount(1, others...) 193 for _, other := range others { 194 left, right := n.compareGet(first, other) 195 if !(left < right) { 196 return false 197 } 198 } 199 return true 200 } 201 202 func (n *Namespace) checkComparisonArgCount(min int, others ...interface{}) bool { 203 if len(others) < min { 204 panic("missing arguments for comparison") 205 } 206 return true 207 } 208 209 // Conditional can be used as a ternary operator. 210 // It returns a if condition, else b. 211 func (n *Namespace) Conditional(condition bool, a, b interface{}) interface{} { 212 if condition { 213 return a 214 } 215 return b 216 } 217 218 func (ns *Namespace) compareGet(a interface{}, b interface{}) (float64, float64) { 219 if ac, ok := a.(compare.Comparer); ok { 220 c := ac.Compare(b) 221 if c < 0 { 222 return 1, 0 223 } else if c == 0 { 224 return 0, 0 225 } else { 226 return 0, 1 227 } 228 } 229 230 if bc, ok := b.(compare.Comparer); ok { 231 c := bc.Compare(a) 232 if c < 0 { 233 return 0, 1 234 } else if c == 0 { 235 return 0, 0 236 } else { 237 return 1, 0 238 } 239 } 240 241 var left, right float64 242 var leftStr, rightStr *string 243 av := reflect.ValueOf(a) 244 245 switch av.Kind() { 246 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 247 left = float64(av.Len()) 248 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 249 left = float64(av.Int()) 250 case reflect.Float32, reflect.Float64: 251 left = av.Float() 252 case reflect.String: 253 var err error 254 left, err = strconv.ParseFloat(av.String(), 64) 255 if err != nil { 256 str := av.String() 257 leftStr = &str 258 } 259 case reflect.Struct: 260 switch av.Type() { 261 case timeType: 262 left = float64(toTimeUnix(av)) 263 } 264 case reflect.Bool: 265 left = 0 266 if av.Bool() { 267 left = 1 268 } 269 } 270 271 bv := reflect.ValueOf(b) 272 273 switch bv.Kind() { 274 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 275 right = float64(bv.Len()) 276 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 277 right = float64(bv.Int()) 278 case reflect.Float32, reflect.Float64: 279 right = bv.Float() 280 case reflect.String: 281 var err error 282 right, err = strconv.ParseFloat(bv.String(), 64) 283 if err != nil { 284 str := bv.String() 285 rightStr = &str 286 } 287 case reflect.Struct: 288 switch bv.Type() { 289 case timeType: 290 right = float64(toTimeUnix(bv)) 291 } 292 case reflect.Bool: 293 right = 0 294 if bv.Bool() { 295 right = 1 296 } 297 } 298 299 if ns.caseInsensitive && leftStr != nil && rightStr != nil { 300 c := compare.Strings(*leftStr, *rightStr) 301 if c < 0 { 302 return 0, 1 303 } else if c > 0 { 304 return 1, 0 305 } else { 306 return 0, 0 307 } 308 } 309 310 switch { 311 case leftStr == nil || rightStr == nil: 312 case *leftStr < *rightStr: 313 return 0, 1 314 case *leftStr > *rightStr: 315 return 1, 0 316 default: 317 return 0, 0 318 } 319 320 return left, right 321 } 322 323 var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() 324 325 func toTimeUnix(v reflect.Value) int64 { 326 if v.Kind() == reflect.Interface { 327 return toTimeUnix(v.Elem()) 328 } 329 if v.Type() != timeType { 330 panic("coding error: argument must be time.Time type reflect Value") 331 } 332 return v.MethodByName("Unix").Call([]reflect.Value{})[0].Int() 333 }