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 }