Files
Курнат Андрей 2315f25754 Initial QSfera import
2026-06-07 10:20:04 +03:00

170 lines
3.1 KiB
Go

// 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))
}