github.com/graemephi/kahugo@v0.62.3-0.20211121071557-d78c0423784d/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 if len(others) == 0 { 99 panic("missing arguments for comparison") 100 } 101 102 normalize := func(v interface{}) interface{} { 103 if types.IsNil(v) { 104 return nil 105 } 106 vv := reflect.ValueOf(v) 107 switch vv.Kind() { 108 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 109 return vv.Int() 110 case reflect.Float32, reflect.Float64: 111 return vv.Float() 112 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 113 return vv.Uint() 114 case reflect.String: 115 return vv.String() 116 default: 117 return v 118 } 119 } 120 121 normFirst := normalize(first) 122 for _, other := range others { 123 if e, ok := first.(compare.Eqer); ok { 124 if e.Eq(other) { 125 return true 126 } 127 continue 128 } 129 130 if e, ok := other.(compare.Eqer); ok { 131 if e.Eq(first) { 132 return true 133 } 134 continue 135 } 136 137 other = normalize(other) 138 if reflect.DeepEqual(normFirst, other) { 139 return true 140 } 141 } 142 143 return false 144 } 145 146 // Ne returns the boolean truth of arg1 != arg2 && arg1 != arg3 && arg1 != arg4. 147 func (n *Namespace) Ne(first interface{}, others ...interface{}) bool { 148 for _, other := range others { 149 if n.Eq(first, other) { 150 return false 151 } 152 } 153 return true 154 } 155 156 // Ge returns the boolean truth of arg1 >= arg2 && arg1 >= arg3 && arg1 >= arg4. 157 func (n *Namespace) Ge(first interface{}, others ...interface{}) bool { 158 for _, other := range others { 159 left, right := n.compareGet(first, other) 160 if !(left >= right) { 161 return false 162 } 163 } 164 return true 165 } 166 167 // Gt returns the boolean truth of arg1 > arg2 && arg1 > arg3 && arg1 > arg4. 168 func (n *Namespace) Gt(first interface{}, others ...interface{}) bool { 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 for _, other := range others { 181 left, right := n.compareGet(first, other) 182 if !(left <= right) { 183 return false 184 } 185 } 186 return true 187 } 188 189 // Lt returns the boolean truth of arg1 < arg2 && arg1 < arg3 && arg1 < arg4. 190 func (n *Namespace) Lt(first interface{}, others ...interface{}) bool { 191 for _, other := range others { 192 left, right := n.compareGet(first, other) 193 if !(left < right) { 194 return false 195 } 196 } 197 return true 198 } 199 200 // Conditional can be used as a ternary operator. 201 // It returns a if condition, else b. 202 func (n *Namespace) Conditional(condition bool, a, b interface{}) interface{} { 203 if condition { 204 return a 205 } 206 return b 207 } 208 209 func (ns *Namespace) compareGet(a interface{}, b interface{}) (float64, float64) { 210 if ac, ok := a.(compare.Comparer); ok { 211 c := ac.Compare(b) 212 if c < 0 { 213 return 1, 0 214 } else if c == 0 { 215 return 0, 0 216 } else { 217 return 0, 1 218 } 219 } 220 221 if bc, ok := b.(compare.Comparer); ok { 222 c := bc.Compare(a) 223 if c < 0 { 224 return 0, 1 225 } else if c == 0 { 226 return 0, 0 227 } else { 228 return 1, 0 229 } 230 } 231 232 var left, right float64 233 var leftStr, rightStr *string 234 av := reflect.ValueOf(a) 235 236 switch av.Kind() { 237 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 238 left = float64(av.Len()) 239 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 240 left = float64(av.Int()) 241 case reflect.Float32, reflect.Float64: 242 left = av.Float() 243 case reflect.String: 244 var err error 245 left, err = strconv.ParseFloat(av.String(), 64) 246 if err != nil { 247 str := av.String() 248 leftStr = &str 249 } 250 case reflect.Struct: 251 switch av.Type() { 252 case timeType: 253 left = float64(toTimeUnix(av)) 254 } 255 case reflect.Bool: 256 left = 0 257 if av.Bool() { 258 left = 1 259 } 260 } 261 262 bv := reflect.ValueOf(b) 263 264 switch bv.Kind() { 265 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: 266 right = float64(bv.Len()) 267 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 268 right = float64(bv.Int()) 269 case reflect.Float32, reflect.Float64: 270 right = bv.Float() 271 case reflect.String: 272 var err error 273 right, err = strconv.ParseFloat(bv.String(), 64) 274 if err != nil { 275 str := bv.String() 276 rightStr = &str 277 } 278 case reflect.Struct: 279 switch bv.Type() { 280 case timeType: 281 right = float64(toTimeUnix(bv)) 282 } 283 case reflect.Bool: 284 right = 0 285 if bv.Bool() { 286 right = 1 287 } 288 } 289 290 if ns.caseInsensitive && leftStr != nil && rightStr != nil { 291 c := compare.Strings(*leftStr, *rightStr) 292 if c < 0 { 293 return 0, 1 294 } else if c > 0 { 295 return 1, 0 296 } else { 297 return 0, 0 298 } 299 } 300 301 switch { 302 case leftStr == nil || rightStr == nil: 303 case *leftStr < *rightStr: 304 return 0, 1 305 case *leftStr > *rightStr: 306 return 1, 0 307 default: 308 return 0, 0 309 } 310 311 return left, right 312 } 313 314 var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() 315 316 func toTimeUnix(v reflect.Value) int64 { 317 if v.Kind() == reflect.Interface { 318 return toTimeUnix(v.Elem()) 319 } 320 if v.Type() != timeType { 321 panic("coding error: argument must be time.Time type reflect Value") 322 } 323 return v.MethodByName("Unix").Call([]reflect.Value{})[0].Int() 324 }