blob: 09ef1fc0515fae690cffda7d528a6247afff0583 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
2 * Copyright 2019-present Ciena Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package order
17
18import (
19 "fmt"
20 "reflect"
21 "sort"
22 "strings"
23)
24
25type Operation int
26
27const (
28 ASC Operation = iota
29 DSC
30)
31
32type SortTerm struct {
33 Op Operation
34 Name string
35}
36
37func (o Operation) String() string {
38 switch o {
39 default:
40 fallthrough
41 case ASC:
42 return "ASC"
43 case DSC:
44 return "DSC"
45 }
46}
47
48type Sorter []SortTerm
49
50func split(term string) SortTerm {
51 st := SortTerm{}
52 if len(term) > 0 {
53 switch term[0] {
54 case '+':
55 fallthrough
56 case '>':
57 st.Op = ASC
58 st.Name = term[1:]
59 case '-':
60 fallthrough
61 case '<':
62 st.Op = DSC
63 st.Name = term[1:]
64 default:
65 st.Op = ASC
66 st.Name = term
67 }
68 } else {
69 st.Op = ASC
70 st.Name = term
71 }
72 return st
73}
74
75// Parse parses a comma separated list of filter terms
76func Parse(spec string) (Sorter, error) {
77 terms := strings.Split(spec, ",")
78 s := make([]SortTerm, 0)
79 for _, term := range terms {
80 s = append(s, split(term))
81 }
82
83 return s, nil
84}
85
Scott Baker9a2d9a42020-06-09 18:11:26 -070086func (s Sorter) GetField(val reflect.Value, name string) (reflect.Value, error) {
87 // If the user gave us an explicitly named dotted field, then split it
88 if strings.Contains(name, ".") {
89 parts := strings.SplitN(name, ".", 2)
90
91 if val.Kind() != reflect.Struct {
92 return val, fmt.Errorf("Dotted field name specified in filter did not resolve to a valid field")
93 }
94
95 field := val.FieldByName(parts[0])
96 if !field.IsValid() {
97 return field, fmt.Errorf("Failed to find dotted field %s while sorting", parts[0])
98 }
99 if field.Kind() == reflect.Ptr {
100 field = reflect.Indirect(field)
101 }
102 return s.GetField(field, parts[1])
103 }
104
105 if val.Kind() != reflect.Struct {
106 return val, fmt.Errorf("Dotted field name specified in filter did not resolve to a valid field")
107 }
108
109 field := val.FieldByName(name)
110 if !field.IsValid() {
111 return field, fmt.Errorf("Failed to find field %s while sorting", name)
112 }
Scott Bakerd466f402020-06-12 15:40:42 -0700113
114 // we might have a pointer to a struct at this time, so dereference it
115 if field.Kind() == reflect.Ptr {
116 field = reflect.Indirect(field)
117 }
118
119 if field.Kind() == reflect.Struct {
120 return val, fmt.Errorf("Cannot sort on a field that is a struct")
121 }
122
Scott Baker9a2d9a42020-06-09 18:11:26 -0700123 return field, nil
124}
125
Zack Williamse940c7a2019-08-21 14:25:39 -0700126func (s Sorter) Process(data interface{}) (interface{}, error) {
127 slice := reflect.ValueOf(data)
128 if slice.Kind() != reflect.Slice {
129 return data, nil
130 }
131
Scott Baker9a2d9a42020-06-09 18:11:26 -0700132 var sortError error = nil
133
Zack Williamse940c7a2019-08-21 14:25:39 -0700134 sort.SliceStable(data, func(i, j int) bool {
135 left := reflect.ValueOf(slice.Index(i).Interface())
136 right := reflect.ValueOf(slice.Index(j).Interface())
Scott Baker34539bf2020-06-04 23:08:57 -0700137
138 if left.Kind() == reflect.Ptr {
139 left = reflect.Indirect(left)
140 }
141
142 if right.Kind() == reflect.Ptr {
143 right = reflect.Indirect(right)
144 }
145
Zack Williamse940c7a2019-08-21 14:25:39 -0700146 for _, term := range s {
Scott Baker9a2d9a42020-06-09 18:11:26 -0700147 fleft, err := s.GetField(left, term.Name)
148 if err != nil {
149 sortError = err
150 return false
151 }
152
153 fright, err := s.GetField(right, term.Name)
154 if err != nil {
155 sortError = err
156 return false
157 }
158
Zack Williamse940c7a2019-08-21 14:25:39 -0700159 switch fleft.Kind() {
160 case reflect.Uint:
161 fallthrough
162 case reflect.Uint8:
163 fallthrough
164 case reflect.Uint16:
165 fallthrough
166 case reflect.Uint32:
167 fallthrough
168 case reflect.Uint64:
169 ileft := fleft.Uint()
170 iright := fright.Uint()
171 switch term.Op {
172 case ASC:
173 if ileft < iright {
174 return true
175 } else if ileft > iright {
176 return false
177 }
178 case DSC:
179 if ileft > iright {
180 return true
181 } else if ileft < iright {
182 return false
183 }
184 }
185 case reflect.Int:
186 fallthrough
187 case reflect.Int8:
188 fallthrough
189 case reflect.Int16:
190 fallthrough
191 case reflect.Int32:
192 fallthrough
193 case reflect.Int64:
194 ileft := fleft.Int()
195 iright := fright.Int()
196 switch term.Op {
197 case ASC:
198 if ileft < iright {
199 return true
200 } else if ileft > iright {
201 return false
202 }
203 case DSC:
204 if ileft > iright {
205 return true
206 } else if ileft < iright {
207 return false
208 }
209 }
210 default:
Scott Baker9a2d9a42020-06-09 18:11:26 -0700211 sleft := fmt.Sprintf("%v", fleft)
212 sright := fmt.Sprintf("%v", fright)
Zack Williamse940c7a2019-08-21 14:25:39 -0700213 diff := strings.Compare(sleft, sright)
214 if term.Op != DSC {
215 if diff == -1 {
216 return true
217 } else if diff == 1 {
218 return false
219 }
220 } else {
221 if diff == 1 {
222 return true
223 } else if diff == -1 {
224 return false
225 }
226 }
227 }
228 }
229 return false
230 })
231
Scott Baker9a2d9a42020-06-09 18:11:26 -0700232 if sortError != nil {
233 return nil, sortError
234 }
235
Zack Williamse940c7a2019-08-21 14:25:39 -0700236 return data, nil
237}