Initial QSfera import
This commit is contained in:
+169
@@ -0,0 +1,169 @@
|
||||
// Copyright 2016 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Compare returns 0 if a equals b, -1 if a is less than b, and 1 if b is than a.
|
||||
//
|
||||
// For comparison between values of different types, the following ordering is used:
|
||||
// nil < bool < int, float64 < string < []any < map[string]any. Slices and maps
|
||||
// are compared recursively. If one slice or map is a subset of the other slice or map
|
||||
// it is considered "less than". Nil is always equal to nil.
|
||||
func Compare(a, b any) int {
|
||||
aSortOrder := sortOrder(a)
|
||||
bSortOrder := sortOrder(b)
|
||||
if aSortOrder < bSortOrder {
|
||||
return -1
|
||||
} else if bSortOrder < aSortOrder {
|
||||
return 1
|
||||
}
|
||||
switch a := a.(type) {
|
||||
case nil:
|
||||
return 0
|
||||
case bool:
|
||||
switch b := b.(type) {
|
||||
case bool:
|
||||
if a == b {
|
||||
return 0
|
||||
}
|
||||
if !a {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
case json.Number:
|
||||
switch b := b.(type) {
|
||||
case json.Number:
|
||||
return compareJSONNumber(a, b)
|
||||
}
|
||||
case int:
|
||||
switch b := b.(type) {
|
||||
case int:
|
||||
if a == b {
|
||||
return 0
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
case float64:
|
||||
switch b := b.(type) {
|
||||
case float64:
|
||||
if a == b {
|
||||
return 0
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
case string:
|
||||
switch b := b.(type) {
|
||||
case string:
|
||||
if a == b {
|
||||
return 0
|
||||
} else if a < b {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
case []any:
|
||||
switch b := b.(type) {
|
||||
case []any:
|
||||
bLen := len(b)
|
||||
aLen := len(a)
|
||||
minLen := min(bLen, aLen)
|
||||
for i := range minLen {
|
||||
cmp := Compare(a[i], b[i])
|
||||
if cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if aLen == bLen {
|
||||
return 0
|
||||
} else if aLen < bLen {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
case map[string]any:
|
||||
switch b := b.(type) {
|
||||
case map[string]any:
|
||||
aKeys := KeysSorted(a)
|
||||
bKeys := KeysSorted(b)
|
||||
aLen := len(aKeys)
|
||||
bLen := len(bKeys)
|
||||
minLen := min(bLen, aLen)
|
||||
for i := range minLen {
|
||||
if aKeys[i] < bKeys[i] {
|
||||
return -1
|
||||
} else if bKeys[i] < aKeys[i] {
|
||||
return 1
|
||||
}
|
||||
aVal := a[aKeys[i]]
|
||||
bVal := b[bKeys[i]]
|
||||
cmp := Compare(aVal, bVal)
|
||||
if cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
}
|
||||
if aLen == bLen {
|
||||
return 0
|
||||
} else if aLen < bLen {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("illegal arguments of type %T and type %T", a, b))
|
||||
}
|
||||
|
||||
const (
|
||||
nilSort = iota
|
||||
boolSort = iota
|
||||
numberSort = iota
|
||||
stringSort = iota
|
||||
arraySort = iota
|
||||
objectSort = iota
|
||||
)
|
||||
|
||||
func compareJSONNumber(a, b json.Number) int {
|
||||
bigA, ok := new(big.Float).SetString(string(a))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
bigB, ok := new(big.Float).SetString(string(b))
|
||||
if !ok {
|
||||
panic("illegal value")
|
||||
}
|
||||
return bigA.Cmp(bigB)
|
||||
}
|
||||
|
||||
func sortOrder(v any) int {
|
||||
switch v.(type) {
|
||||
case nil:
|
||||
return nilSort
|
||||
case bool:
|
||||
return boolSort
|
||||
case json.Number:
|
||||
return numberSort
|
||||
case int:
|
||||
return numberSort
|
||||
case float64:
|
||||
return numberSort
|
||||
case string:
|
||||
return stringSort
|
||||
case []any:
|
||||
return arraySort
|
||||
case map[string]any:
|
||||
return objectSort
|
||||
}
|
||||
panic(fmt.Sprintf("illegal argument of type %T", v))
|
||||
}
|
||||
Reference in New Issue
Block a user