sort.go - hugo - [fork] hugo port for 9front
 (HTM) git clone https://git.drkhsh.at/hugo.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
 (DIR) README
 (DIR) LICENSE
       ---
       sort.go (5037B)
       ---
            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 := range seqv.Len() {
           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 
          103                 iter := seqv.MapRange()
          104                 i := 0
          105                 for iter.Next() {
          106                         key := iter.Key()
          107                         value := iter.Value()
          108                         p.Pairs[i].Value = value
          109                         if sortByField == "" {
          110                                 p.Pairs[i].Key = key
          111                         } else if sortByField == "value" {
          112                                 p.Pairs[i].Key = p.Pairs[i].Value
          113                         } else {
          114                                 v := p.Pairs[i].Value
          115                                 var err error
          116                                 for j, elemName := range path {
          117                                         v, err = evaluateSubElem(ctxv, v, elemName)
          118                                         if err != nil {
          119                                                 return nil, err
          120                                         }
          121                                         if !v.IsValid() {
          122                                                 continue
          123                                         }
          124                                         // Special handling of lower cased maps.
          125                                         if params, ok := v.Interface().(maps.Params); ok {
          126                                                 v = reflect.ValueOf(params.GetNested(path[j+1:]...))
          127                                                 break
          128                                         }
          129                                 }
          130                                 p.Pairs[i].Key = v
          131                         }
          132                         i++
          133                 }
          134         }
          135 
          136         collator.Lock()
          137         defer collator.Unlock()
          138 
          139         return p.sort(), nil
          140 }
          141 
          142 // Credit for pair sorting method goes to Andrew Gerrand
          143 // https://groups.google.com/forum/#!topic/golang-nuts/FT7cjmcL7gw
          144 // A data structure to hold a key/value pair.
          145 type pair struct {
          146         Key   reflect.Value
          147         Value reflect.Value
          148 }
          149 
          150 // A slice of pairs that implements sort.Interface to sort by Value.
          151 type pairList struct {
          152         Collator  *langs.Collator
          153         sortComp  *compare.Namespace
          154         Pairs     []pair
          155         SortAsc   bool
          156         SliceType reflect.Type
          157 }
          158 
          159 func (p pairList) Swap(i, j int) { p.Pairs[i], p.Pairs[j] = p.Pairs[j], p.Pairs[i] }
          160 func (p pairList) Len() int      { return len(p.Pairs) }
          161 func (p pairList) Less(i, j int) bool {
          162         iv := p.Pairs[i].Key
          163         jv := p.Pairs[j].Key
          164 
          165         if iv.IsValid() {
          166                 if jv.IsValid() {
          167                         // can only call Interface() on valid reflect Values
          168                         return p.sortComp.LtCollate(p.Collator, iv.Interface(), jv.Interface())
          169                 }
          170 
          171                 // if j is invalid, test i against i's zero value
          172                 return p.sortComp.LtCollate(p.Collator, iv.Interface(), reflect.Zero(iv.Type()))
          173         }
          174 
          175         if jv.IsValid() {
          176                 // if i is invalid, test j against j's zero value
          177                 return p.sortComp.LtCollate(p.Collator, reflect.Zero(jv.Type()), jv.Interface())
          178         }
          179 
          180         return false
          181 }
          182 
          183 // sorts a pairList and returns a slice of sorted values
          184 func (p pairList) sort() any {
          185         if p.SortAsc {
          186                 sort.Stable(p)
          187         } else {
          188                 sort.Stable(sort.Reverse(p))
          189         }
          190         sorted := reflect.MakeSlice(p.SliceType, len(p.Pairs), len(p.Pairs))
          191         for i, v := range p.Pairs {
          192                 sorted.Index(i).Set(v.Value)
          193         }
          194 
          195         return sorted.Interface()
          196 }