-
Notifications
You must be signed in to change notification settings - Fork 78
/
op_sort.go
139 lines (112 loc) · 3.02 KB
/
op_sort.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package spruce
import (
"fmt"
"reflect"
"sort"
"strings"
. "github.com/geofffranks/spruce/log"
"github.com/starkandwayne/goutils/tree"
)
var pathsToSort = map[string]string{}
type itemType int
const (
stringItems itemType = iota
floatItems
intItems
mapItems
otherItems
)
// SortOperator ...
type SortOperator struct{}
// Setup ...
func (SortOperator) Setup() error {
return nil
}
// Phase ...
func (SortOperator) Phase() OperatorPhase {
return MergePhase
}
// Dependencies ...
func (SortOperator) Dependencies(_ *Evaluator, _ []*Expr, _ []*tree.Cursor, auto []*tree.Cursor) []*tree.Cursor {
return auto
}
// Run ...
func (SortOperator) Run(ev *Evaluator, args []*Expr) (*Response, error) {
return nil, fmt.Errorf("orphaned (( sort )) operator at $.%s, no list exists at that path", ev.Here)
}
func init() {
RegisterOp("sort", SortOperator{})
}
func addToSortListIfNecessary(operator string, path string) {
if opcall, err := ParseOpcall(MergePhase, operator); err == nil {
var byKey string
if len(opcall.args) == 2 {
byKey = opcall.args[1].String()
}
DEBUG("adding sort by '%s' of path '%s' to the list of paths to sort", byKey, path)
if _, ok := pathsToSort[path]; !ok {
pathsToSort[path] = byKey
}
}
}
func universalLess(a interface{}, b interface{}, key string) bool {
switch a.(type) {
case string:
return strings.Compare(a.(string), b.(string)) < 0
case float64:
return a.(float64) < b.(float64)
case int:
return a.(int) < b.(int)
case map[interface{}]interface{}:
entryA, entryB := a.(map[interface{}]interface{}), b.(map[interface{}]interface{})
return universalLess(entryA[key], entryB[key], key)
}
return false
}
func sortList(path string, list []interface{}, key string) error {
typeCheckMap := map[string]struct{}{}
for _, entry := range list {
reflectType := reflect.TypeOf(entry)
var typeName string
if reflectType != nil {
typeName = reflectType.Kind().String()
} else {
typeName = "nil"
}
if _, ok := typeCheckMap[typeName]; !ok {
typeCheckMap[typeName] = struct{}{}
}
}
if length := len(typeCheckMap); length > 0 && length != 1 {
return tree.TypeMismatchError{
Path: []string{path},
Wanted: "a list with homogeneous entry types",
Got: "a list with different types",
}
}
for kind := range typeCheckMap {
switch kind {
case reflect.Map.String():
if key == "" {
key = getDefaultIdentifierKey()
}
if err := canKeyMergeArray("list", list, path, key); err != nil {
return tree.TypeMismatchError{
Path: []string{path},
Wanted: fmt.Sprintf("a list with map entries each containing %s", key),
Got: fmt.Sprintf("a list with map entries, where some do not contain %s", key),
}
}
case reflect.Slice.String():
return tree.TypeMismatchError{
Path: []string{path},
Wanted: fmt.Sprintf("a list with maps, strings or numbers"),
Got: fmt.Sprintf("a list with list entries"),
}
}
}
sort.Slice(list, func(i int, j int) bool {
return universalLess(list[i], list[j], key)
})
return nil
}