github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/tpl/collections/sort.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 collections 15 16 import ( 17 "context" 18 "errors" 19 "reflect" 20 "sort" 21 "strings" 22 23 "github.com/gohugoio/hugo/common/maps" 24 "github.com/gohugoio/hugo/langs" 25 "github.com/gohugoio/hugo/tpl/compare" 26 "github.com/spf13/cast" 27 ) 28 29 // Sort returns a sorted copy of the list l. 30 func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error) { 31 if l == nil { 32 return nil, errors.New("sequence must be provided") 33 } 34 35 seqv, isNil := indirect(reflect.ValueOf(l)) 36 if isNil { 37 return nil, errors.New("can't iterate over a nil value") 38 } 39 40 ctxv := reflect.ValueOf(ctx) 41 42 var sliceType reflect.Type 43 switch seqv.Kind() { 44 case reflect.Array, reflect.Slice: 45 sliceType = seqv.Type() 46 case reflect.Map: 47 sliceType = reflect.SliceOf(seqv.Type().Elem()) 48 default: 49 return nil, errors.New("can't sort " + reflect.ValueOf(l).Type().String()) 50 } 51 52 collator := langs.GetCollator1(ns.deps.Conf.Language()) 53 54 // Create a list of pairs that will be used to do the sort 55 p := pairList{Collator: collator, sortComp: ns.sortComp, SortAsc: true, SliceType: sliceType} 56 p.Pairs = make([]pair, seqv.Len()) 57 58 var sortByField string 59 for i, l := range args { 60 dStr, err := cast.ToStringE(l) 61 switch { 62 case i == 0 && err != nil: 63 sortByField = "" 64 case i == 0 && err == nil: 65 sortByField = dStr 66 case i == 1 && err == nil && dStr == "desc": 67 p.SortAsc = false 68 case i == 1: 69 p.SortAsc = true 70 } 71 } 72 path := strings.Split(strings.Trim(sortByField, "."), ".") 73 74 switch seqv.Kind() { 75 case reflect.Array, reflect.Slice: 76 for i := 0; i < seqv.Len(); i++ { 77 p.Pairs[i].Value = seqv.Index(i) 78 if sortByField == "" || sortByField == "value" { 79 p.Pairs[i].Key = p.Pairs[i].Value 80 } else { 81 v := p.Pairs[i].Value 82 var err error 83 for i, elemName := range path { 84 v, err = evaluateSubElem(ctxv, v, elemName) 85 if err != nil { 86 return nil, err 87 } 88 if !v.IsValid() { 89 continue 90 } 91 // Special handling of lower cased maps. 92 if params, ok := v.Interface().(maps.Params); ok { 93 v = reflect.ValueOf(params.GetNested(path[i+1:]...)) 94 break 95 } 96 } 97 p.Pairs[i].Key = v 98 } 99 } 100 101 case reflect.Map: 102 keys := seqv.MapKeys() 103 for i := 0; i < seqv.Len(); i++ { 104 p.Pairs[i].Value = seqv.MapIndex(keys[i]) 105 106 if sortByField == "" { 107 p.Pairs[i].Key = keys[i] 108 } else if sortByField == "value" { 109 p.Pairs[i].Key = p.Pairs[i].Value 110 } else { 111 v := p.Pairs[i].Value 112 var err error 113 for i, elemName := range path { 114 v, err = evaluateSubElem(ctxv, v, elemName) 115 if err != nil { 116 return nil, err 117 } 118 if !v.IsValid() { 119 continue 120 } 121 // Special handling of lower cased maps. 122 if params, ok := v.Interface().(maps.Params); ok { 123 v = reflect.ValueOf(params.GetNested(path[i+1:]...)) 124 break 125 } 126 } 127 p.Pairs[i].Key = v 128 } 129 } 130 } 131 132 collator.Lock() 133 defer collator.Unlock() 134 135 return p.sort(), nil 136 } 137 138 // Credit for pair sorting method goes to Andrew Gerrand 139 // https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw 140 // A data structure to hold a key/value pair. 141 type pair struct { 142 Key reflect.Value 143 Value reflect.Value 144 } 145 146 // A slice of pairs that implements sort.Interface to sort by Value. 147 type pairList struct { 148 Collator *langs.Collator 149 sortComp *compare.Namespace 150 Pairs []pair 151 SortAsc bool 152 SliceType reflect.Type 153 } 154 155 func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] } 156 func (p pairList) Len() int { return len(p.Pairs) } 157 func (p pairList) Less(i, j int) bool { 158 iv := p.Pairs[i].Key 159 jv := p.Pairs[j].Key 160 161 if iv.IsValid() { 162 if jv.IsValid() { 163 // can only call Interface() on valid reflect Values 164 return p.sortComp.LtCollate(p.Collator, iv.Interface(), jv.Interface()) 165 } 166 167 // if j is invalid, test i against i's zero value 168 return p.sortComp.LtCollate(p.Collator, iv.Interface(), reflect.Zero(iv.Type())) 169 } 170 171 if jv.IsValid() { 172 // if i is invalid, test j against j's zero value 173 return p.sortComp.LtCollate(p.Collator, reflect.Zero(jv.Type()), jv.Interface()) 174 } 175 176 return false 177 } 178 179 // sorts a pairList and returns a slice of sorted values 180 func (p pairList) sort() any { 181 if p.SortAsc { 182 sort.Stable(p) 183 } else { 184 sort.Stable(sort.Reverse(p)) 185 } 186 sorted := reflect.MakeSlice(p.SliceType, len(p.Pairs), len(p.Pairs)) 187 for i, v := range p.Pairs { 188 sorted.Index(i).Set(v.Value) 189 } 190 191 return sorted.Interface() 192 }