blob: bcf097b10fda28bef6e07931f22a4a1d55a8da94 [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 }
113 return field, nil
114}
115
Zack Williamse940c7a2019-08-21 14:25:39 -0700116func (s Sorter) Process(data interface{}) (interface{}, error) {
117 slice := reflect.ValueOf(data)
118 if slice.Kind() != reflect.Slice {
119 return data, nil
120 }
121
Scott Baker9a2d9a42020-06-09 18:11:26 -0700122 var sortError error = nil
123
Zack Williamse940c7a2019-08-21 14:25:39 -0700124 sort.SliceStable(data, func(i, j int) bool {
125 left := reflect.ValueOf(slice.Index(i).Interface())
126 right := reflect.ValueOf(slice.Index(j).Interface())
Scott Baker34539bf2020-06-04 23:08:57 -0700127
128 if left.Kind() == reflect.Ptr {
129 left = reflect.Indirect(left)
130 }
131
132 if right.Kind() == reflect.Ptr {
133 right = reflect.Indirect(right)
134 }
135
Zack Williamse940c7a2019-08-21 14:25:39 -0700136 for _, term := range s {
Scott Baker9a2d9a42020-06-09 18:11:26 -0700137 fleft, err := s.GetField(left, term.Name)
138 if err != nil {
139 sortError = err
140 return false
141 }
142
143 fright, err := s.GetField(right, term.Name)
144 if err != nil {
145 sortError = err
146 return false
147 }
148
Zack Williamse940c7a2019-08-21 14:25:39 -0700149 switch fleft.Kind() {
150 case reflect.Uint:
151 fallthrough
152 case reflect.Uint8:
153 fallthrough
154 case reflect.Uint16:
155 fallthrough
156 case reflect.Uint32:
157 fallthrough
158 case reflect.Uint64:
159 ileft := fleft.Uint()
160 iright := fright.Uint()
161 switch term.Op {
162 case ASC:
163 if ileft < iright {
164 return true
165 } else if ileft > iright {
166 return false
167 }
168 case DSC:
169 if ileft > iright {
170 return true
171 } else if ileft < iright {
172 return false
173 }
174 }
175 case reflect.Int:
176 fallthrough
177 case reflect.Int8:
178 fallthrough
179 case reflect.Int16:
180 fallthrough
181 case reflect.Int32:
182 fallthrough
183 case reflect.Int64:
184 ileft := fleft.Int()
185 iright := fright.Int()
186 switch term.Op {
187 case ASC:
188 if ileft < iright {
189 return true
190 } else if ileft > iright {
191 return false
192 }
193 case DSC:
194 if ileft > iright {
195 return true
196 } else if ileft < iright {
197 return false
198 }
199 }
200 default:
Scott Baker9a2d9a42020-06-09 18:11:26 -0700201 sleft := fmt.Sprintf("%v", fleft)
202 sright := fmt.Sprintf("%v", fright)
Zack Williamse940c7a2019-08-21 14:25:39 -0700203 diff := strings.Compare(sleft, sright)
204 if term.Op != DSC {
205 if diff == -1 {
206 return true
207 } else if diff == 1 {
208 return false
209 }
210 } else {
211 if diff == 1 {
212 return true
213 } else if diff == -1 {
214 return false
215 }
216 }
217 }
218 }
219 return false
220 })
221
Scott Baker9a2d9a42020-06-09 18:11:26 -0700222 if sortError != nil {
223 return nil, sortError
224 }
225
Zack Williamse940c7a2019-08-21 14:25:39 -0700226 return data, nil
227}