Module split
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
cover.out
|
||||||
|
cover.html
|
||||||
38
.golangci.yaml
Normal file
38
.golangci.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
# re-enable when working
|
||||||
|
- rowserrcheck
|
||||||
|
- wastedassign
|
||||||
|
# maybe enable these
|
||||||
|
- wrapcheck
|
||||||
|
# leave these disabled
|
||||||
|
- cyclop
|
||||||
|
- deadcode
|
||||||
|
- dupl
|
||||||
|
- exhaustivestruct
|
||||||
|
- exhaustruct
|
||||||
|
- forbidigo
|
||||||
|
- forcetypeassert
|
||||||
|
- funlen
|
||||||
|
- gochecknoglobals
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- godox
|
||||||
|
- golint
|
||||||
|
- gomnd
|
||||||
|
- ifshort
|
||||||
|
- interfacer
|
||||||
|
- lll
|
||||||
|
- maintidx
|
||||||
|
- maligned
|
||||||
|
- nilnil
|
||||||
|
- nestif
|
||||||
|
- nlreturn
|
||||||
|
- nolintlint
|
||||||
|
- nosnakecase
|
||||||
|
- scopelint
|
||||||
|
- structcheck
|
||||||
|
- thelper
|
||||||
|
- varcheck
|
||||||
|
- varnamelen
|
||||||
20
equal.go
Normal file
20
equal.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func Equal(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, equal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func equal(obj, match any, _ string) bool {
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case time.Time:
|
||||||
|
tm := match.(*timeVal)
|
||||||
|
|
||||||
|
// TODO: Replace Truncate() with a timezone-aware version
|
||||||
|
return tm.time.Equal(objt.Truncate(tm.precision))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obj == match
|
||||||
|
}
|
||||||
|
}
|
||||||
464
equal_test.go
Normal file
464
equal_test.go
Normal file
@@ -0,0 +1,464 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEqualStruct(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType2{
|
||||||
|
Tt1: testType1{
|
||||||
|
Int: 2345,
|
||||||
|
},
|
||||||
|
}, "tt1.int", "2345")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType2{
|
||||||
|
Tt1p: &testType1{
|
||||||
|
Int: 2345,
|
||||||
|
},
|
||||||
|
}, "tt1p.int", "2345")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType2{}, "tt1p.int", "2345")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualPointer(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
TimeP: &tm,
|
||||||
|
}, "timep", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
TimeP: &tm,
|
||||||
|
}, "timep", "2006-01-02T15:04:05+01:00")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualPointers(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm1, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
TimesP: []*time.Time{&tm1, nil, &tm2},
|
||||||
|
}, "timesp", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
TimesP: []*time.Time{&tm1, &tm2},
|
||||||
|
}, "timesp", "2006-01-02T15:04:05+01:00")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1234")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3456")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4567")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5678")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1415")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159265")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
|
||||||
|
boolp := true
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
BoolP: &boolp,
|
||||||
|
}, "boolp", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{}, "boolp", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "4")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "4")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "4")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "4")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7182")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7182")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Bools: []bool{false, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:05+00:00")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:05+01:00")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "1136214245")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "1136214246")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "1136214245000")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "1136214245001")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05.999999999Z", "2006-01-02T15:04:05.500000000Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Time: tm2,
|
||||||
|
}, "time", "1136214245")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "1136214245000")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "1136214245001")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqualDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Equal(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Equal(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
19
go.mod
Normal file
19
go.mod
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module github.com/gopatchy/path
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.110.0
|
||||||
|
github.com/gopatchy/jsrest v0.0.0-20230420161234-12a6d6da8b7f
|
||||||
|
github.com/stretchr/testify v1.8.2
|
||||||
|
go.uber.org/goleak v1.2.1
|
||||||
|
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/gopatchy/metadata v0.0.0-20230420053349-25837551c11d // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/vfaronov/httpheader v0.1.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
33
go.sum
Normal file
33
go.sum
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||||
|
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/gopatchy/jsrest v0.0.0-20230420161234-12a6d6da8b7f h1:1uGPJm9K0Fro1UEcZpuK6FNPU/U1XX3aS3x0/PdFS40=
|
||||||
|
github.com/gopatchy/jsrest v0.0.0-20230420161234-12a6d6da8b7f/go.mod h1:Ryi8LRBLFDhQsMQHuh+6VL7HcFWjBXOEiOy9Ip/Q+Ps=
|
||||||
|
github.com/gopatchy/metadata v0.0.0-20230420053349-25837551c11d h1:chunoM47vkWSanIvLx4uRSkLMG6chDZOy09L2tt/bv8=
|
||||||
|
github.com/gopatchy/metadata v0.0.0-20230420053349-25837551c11d/go.mod h1:VgD33raUShjDePCDBo55aj+eSXFtUEpMzs+Ie39g2zo=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/vfaronov/httpheader v0.1.0 h1:VdzetvOKRoQVHjSrXcIOwCV6JG5BCAW9rjbVbFPBmb0=
|
||||||
|
github.com/vfaronov/httpheader v0.1.0/go.mod h1:ZBxgbYu6nbN5V9Ptd1yYUUan0voD0O8nZLXHyxLgoLE=
|
||||||
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
|
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb h1:rhjz/8Mbfa8xROFiH+MQphmAmgqRM0bOMnytznhWEXk=
|
||||||
|
golang.org/x/exp v0.0.0-20230420155640-133eef4313cb/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
50
greater.go
Normal file
50
greater.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Greater(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, greater)
|
||||||
|
}
|
||||||
|
|
||||||
|
func greater(obj, match any, _ string) bool {
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case int:
|
||||||
|
return objt > match.(int)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return objt > match.(int64)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return objt > match.(uint)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return objt > match.(uint64)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return objt > match.(float32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return objt > match.(float64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return objt > match.(string)
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
return objt && !match.(bool)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
tm := match.(*timeVal)
|
||||||
|
|
||||||
|
return objt.Truncate(tm.precision).After(tm.time)
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
return objt.After(match.(civil.Date))
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
348
greater_test.go
Normal file
348
greater_test.go
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGreaterInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Bool: false,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-05T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-11T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Greater(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Greater(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-04")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
51
greaterequal.go
Normal file
51
greaterequal.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GreaterEqual(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, greaterEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func greaterEqual(obj, match any, _ string) bool {
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case int:
|
||||||
|
return objt >= match.(int)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return objt >= match.(int64)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return objt >= match.(uint)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return objt >= match.(uint64)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return objt >= match.(float32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return objt >= match.(float64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return objt >= match.(string)
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
return objt || objt == match.(bool)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
tm := match.(*timeVal)
|
||||||
|
trunc := objt.Truncate(tm.precision)
|
||||||
|
|
||||||
|
return trunc.Equal(tm.time) || trunc.After(tm.time)
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
return objt == match.(civil.Date) || objt.After(match.(civil.Date))
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
468
greaterequal_test.go
Normal file
468
greaterequal_test.go
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGreaterEqualInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1234")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3456")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4567")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5678")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1415")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159265")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Bool: false,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "7")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "7")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "7")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "7")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "8")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "3.1415")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "3.1415")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Bools: []bool{false, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-05T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-11T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGreaterEqualDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.GreaterEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.GreaterEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-04")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
54
hasprefix.go
Normal file
54
hasprefix.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HasPrefix(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, hasPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPrefix(obj, match any, matchStr string) bool {
|
||||||
|
var objStr string
|
||||||
|
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case int:
|
||||||
|
objStr = strconv.FormatInt(int64(objt), 10)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
objStr = strconv.FormatInt(objt, 10)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
objStr = strconv.FormatUint(uint64(objt), 10)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
objStr = strconv.FormatUint(objt, 10)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
objStr = strconv.FormatFloat(float64(objt), 'f', -1, 32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
objStr = strconv.FormatFloat(objt, 'f', -1, 64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
objStr = objt
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
objStr = strconv.FormatBool(objt)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
objStr = objt.String()
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
objStr = objt.String()
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasPrefix(objStr, matchStr)
|
||||||
|
}
|
||||||
152
hasprefix_test.go
Normal file
152
hasprefix_test.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHasPrefixInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Int: -1234,
|
||||||
|
}, "int", "-12")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Int: -1234,
|
||||||
|
}, "int", "23")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "34")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "45")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "45")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "567")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "56789")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Float32: -3.1415,
|
||||||
|
}, "float32", "-3.14")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Float32: -3.1415,
|
||||||
|
}, "float32", "3.14")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.1415926")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.141592651")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
String: "foobar",
|
||||||
|
}, "string2", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
String: "foobar",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "t")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "f")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefixStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.HasPrefix(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "f")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.HasPrefix(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
5
in.go
Normal file
5
in.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
func In(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return opList(obj, path, matchStr, equal)
|
||||||
|
}
|
||||||
348
in_test.go
Normal file
348
in_test.go
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233,1234,1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233,1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455,3456,3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455,3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566,4567,4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566,4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677,5678,5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677,5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414,3.1415,3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414,3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264,3.14159265,3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264,3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig,foo,bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig,bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "true,false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false,false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "3,4,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "3,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "3,4,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "3,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "3,4,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "3,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "3,4,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "3,5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181,2.7182,2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181,2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181,2.7182,2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181,2.7183")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz,foo,zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz,zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true,false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Bools: []bool{false, false},
|
||||||
|
}, "bools", "true,true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z,2006-01-02T15:04:05Z,2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z,2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-02T15:04:04Z,2006-01-02T15:04:05Z,2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-02T15:04:04Z,2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01,2006-01-02,2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01,2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-05")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.In(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-01,2006-01-02,2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.In(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-01,2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
18
justfile
Normal file
18
justfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
go := env_var_or_default('GOCMD', 'go')
|
||||||
|
|
||||||
|
default: tidy test
|
||||||
|
|
||||||
|
tidy:
|
||||||
|
{{go}} mod tidy
|
||||||
|
goimports -l -w .
|
||||||
|
gofumpt -l -w .
|
||||||
|
{{go}} fmt ./...
|
||||||
|
|
||||||
|
test:
|
||||||
|
{{go}} vet ./...
|
||||||
|
golangci-lint run ./...
|
||||||
|
{{go}} test -race -coverprofile=cover.out -timeout=60s -parallel=10 ./...
|
||||||
|
{{go}} tool cover -html=cover.out -o=cover.html
|
||||||
|
|
||||||
|
todo:
|
||||||
|
-git grep -e TODO --and --not -e ignoretodo
|
||||||
50
less.go
Normal file
50
less.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Less(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, less)
|
||||||
|
}
|
||||||
|
|
||||||
|
func less(obj, match any, _ string) bool {
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case int:
|
||||||
|
return objt < match.(int)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return objt < match.(int64)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return objt < match.(uint)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return objt < match.(uint64)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return objt < match.(float32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return objt < match.(float64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return objt < match.(string)
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
return !objt && match.(bool)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
tm := match.(*timeVal)
|
||||||
|
|
||||||
|
return objt.Truncate(tm.precision).Before(tm.time)
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
return objt.Before(match.(civil.Date))
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
348
less_test.go
Normal file
348
less_test.go
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLessInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Bool: false,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "3")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "3")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "3")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "3")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "adv")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-05T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-01T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-04")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.Less(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.Less(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
51
lessequal.go
Normal file
51
lessequal.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LessEqual(obj any, path string, matchStr string) (bool, error) {
|
||||||
|
return op(obj, path, matchStr, lessEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessEqual(obj, match any, _ string) bool {
|
||||||
|
switch objt := obj.(type) {
|
||||||
|
case int:
|
||||||
|
return objt <= match.(int)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return objt <= match.(int64)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return objt <= match.(uint)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return objt <= match.(uint64)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return objt <= match.(float32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return objt <= match.(float64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return objt <= match.(string)
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
return !objt || objt == match.(bool)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
tm := match.(*timeVal)
|
||||||
|
trunc := objt.Truncate(tm.precision)
|
||||||
|
|
||||||
|
return trunc.Equal(tm.time) || trunc.Before(tm.time)
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
return objt == match.(civil.Date) || objt.Before(match.(civil.Date))
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
468
lessequal_test.go
Normal file
468
lessequal_test.go
Normal file
@@ -0,0 +1,468 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLessEqualInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1235")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1234")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int: 1234,
|
||||||
|
}, "int", "1233")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3457")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3456")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int64: 3456,
|
||||||
|
}, "int64", "3455")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualUInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4568")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4567")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt: 4567,
|
||||||
|
}, "uint", "4566")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualUInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5679")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5678")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt64: 5678,
|
||||||
|
}, "uint64", "5677")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1416")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1415")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float32: 3.1415,
|
||||||
|
}, "float32", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159266")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159265")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float64: 3.14159265,
|
||||||
|
}, "float64", "3.14159264")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "zig")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
String: "foo",
|
||||||
|
}, "string2", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Bool: false,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Bool: true,
|
||||||
|
}, "bool2", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Ints: []int{2, 4, 7},
|
||||||
|
}, "ints", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Int64s: []int64{2, 4, 7},
|
||||||
|
}, "int64s", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualUInts(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInts: []uint{2, 4, 7},
|
||||||
|
}, "uints", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualUInt64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "5")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
UInt64s: []uint64{2, 4, 7},
|
||||||
|
}, "uint64s", "1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualFloat32s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7182")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float32s: []float32{3.1415, 2.7182},
|
||||||
|
}, "float32s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualFloat64s(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "3.1414")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7182")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Float64s: []float64{3.1415, 2.7182},
|
||||||
|
}, "float64s", "2.7181")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualStrings(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "baz")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "bar")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Strings: []string{"foo", "bar"},
|
||||||
|
}, "strings", "adv")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualBools(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Bools: []bool{true, false},
|
||||||
|
}, "bools", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Bools: []bool{true, true},
|
||||||
|
}, "bools", "false")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:06Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Time: tm,
|
||||||
|
}, "time", "2006-01-02T15:04:04Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualTimes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tm, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tm2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-10T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-05T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Times: []time.Time{tm, tm2},
|
||||||
|
}, "times", "2006-01-01T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Date: d,
|
||||||
|
}, "date", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLessEqualDates(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
d2, err := civil.ParseDate("2006-01-04")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
match, err := path.LessEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, match)
|
||||||
|
|
||||||
|
match, err = path.LessEqual(&testType1{
|
||||||
|
Dates: []civil.Date{d1, d2},
|
||||||
|
}, "dates", "2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, match)
|
||||||
|
}
|
||||||
88
merge.go
Normal file
88
merge.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Merge(to, from any) {
|
||||||
|
MergeValue(reflect.ValueOf(to), reflect.ValueOf(from))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeValue(to, from reflect.Value) {
|
||||||
|
to = reflect.Indirect(to)
|
||||||
|
from = reflect.Indirect(from)
|
||||||
|
|
||||||
|
for i := 0; i < to.NumField(); i++ {
|
||||||
|
toField := to.Field(i)
|
||||||
|
fromField := from.Field(i)
|
||||||
|
|
||||||
|
if fromField.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.Indirect(fromField).Kind() == reflect.Struct {
|
||||||
|
MergeValue(toField, fromField)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
toField.Set(fromField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeMap(to any, from map[string]any) error {
|
||||||
|
m, err := ToMap(to)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Wrap error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeMaps(m, from)
|
||||||
|
|
||||||
|
return FromMap(to, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeMaps(to map[string]any, from map[string]any) {
|
||||||
|
for k, v := range from {
|
||||||
|
if vMap, isMap := v.(map[string]any); isMap {
|
||||||
|
if _, ok := to[k].(map[string]any); !ok {
|
||||||
|
// Either key doesn't exist or it's a different type
|
||||||
|
// If different type, error will happen during json decode
|
||||||
|
to[k] = map[string]any{}
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeMaps(to[k].(map[string]any), vMap)
|
||||||
|
} else {
|
||||||
|
to[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToMap(from any) (map[string]any, error) {
|
||||||
|
js, err := json.Marshal(from)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Wrap error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := map[string]any{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(js, &ret)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Wrap error
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromMap(to any, from map[string]any) error {
|
||||||
|
js, err := json.Marshal(from)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Wrap error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Wrap error
|
||||||
|
return json.Unmarshal(js, to)
|
||||||
|
}
|
||||||
137
merge_test.go
Normal file
137
merge_test.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mergeTestType struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
C []string
|
||||||
|
D nestedType
|
||||||
|
E *nestedType
|
||||||
|
H int
|
||||||
|
}
|
||||||
|
|
||||||
|
type nestedType struct {
|
||||||
|
F []int
|
||||||
|
G string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
to := &mergeTestType{
|
||||||
|
A: "foo",
|
||||||
|
B: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Merge(to, &mergeTestType{
|
||||||
|
A: "bar",
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, "bar", to.A)
|
||||||
|
require.Equal(t, 42, to.B)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeSlice(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
to := &mergeTestType{
|
||||||
|
B: 42,
|
||||||
|
C: []string{"foo", "bar"},
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Merge(to, &mergeTestType{
|
||||||
|
C: []string{"zig", "zag"},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, 42, to.B)
|
||||||
|
require.Equal(t, []string{"zig", "zag"}, to.C)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeNested(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
to := &mergeTestType{
|
||||||
|
B: 42,
|
||||||
|
D: nestedType{
|
||||||
|
F: []int{42, 43},
|
||||||
|
G: "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Merge(to, &mergeTestType{
|
||||||
|
D: nestedType{
|
||||||
|
F: []int{44, 45},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, 42, to.B)
|
||||||
|
require.Equal(t, []int{44, 45}, to.D.F)
|
||||||
|
require.Equal(t, "bar", to.D.G)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeNestedPointer(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
to := &mergeTestType{
|
||||||
|
B: 42,
|
||||||
|
E: &nestedType{
|
||||||
|
F: []int{42, 43},
|
||||||
|
G: "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
path.Merge(to, &mergeTestType{
|
||||||
|
E: &nestedType{
|
||||||
|
F: []int{49, 50},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
require.Equal(t, 42, to.B)
|
||||||
|
require.Equal(t, []int{49, 50}, to.E.F)
|
||||||
|
require.Equal(t, "bar", to.E.G)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeMap(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
to := &mergeTestType{
|
||||||
|
A: "foo",
|
||||||
|
B: 42,
|
||||||
|
D: nestedType{
|
||||||
|
F: []int{42, 43},
|
||||||
|
G: "bar",
|
||||||
|
},
|
||||||
|
H: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
from := map[string]any{}
|
||||||
|
|
||||||
|
err := json.Unmarshal(
|
||||||
|
[]byte(`
|
||||||
|
{
|
||||||
|
"B": 45,
|
||||||
|
"D": {
|
||||||
|
"F": [46, 47]
|
||||||
|
},
|
||||||
|
"H": 0
|
||||||
|
}`),
|
||||||
|
&from,
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = path.MergeMap(to, from)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, "foo", to.A)
|
||||||
|
require.Equal(t, 45, to.B)
|
||||||
|
require.Equal(t, []int{46, 47}, to.D.F)
|
||||||
|
require.Equal(t, "bar", to.D.G)
|
||||||
|
require.Equal(t, 0, to.H)
|
||||||
|
}
|
||||||
56
op.go
Normal file
56
op.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func op(obj any, path string, matchStr string, cb func(any, any, string) bool) (bool, error) {
|
||||||
|
objVal, err := Get(obj, path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matchVal, err := parse(matchStr, objVal)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSlice(objVal) {
|
||||||
|
return anyTrue(objVal, func(x any, _ int) bool { return cb(x, matchVal, matchStr) }), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(objVal, matchVal, matchStr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func opList(obj any, path string, matchStr string, cb func(any, any, string) bool) (bool, error) {
|
||||||
|
objVal, err := Get(obj, path)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if objVal == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
matchVal := []any{}
|
||||||
|
matchParts := strings.Split(matchStr, ",")
|
||||||
|
|
||||||
|
for _, matchPart := range matchParts {
|
||||||
|
matchTmp, err := parse(matchPart, objVal)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matchVal = append(matchVal, matchTmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return anyTrue(matchVal, func(y any, i int) bool {
|
||||||
|
str := matchParts[i]
|
||||||
|
|
||||||
|
if isSlice(objVal) {
|
||||||
|
return anyTrue(objVal, func(x any, _ int) bool { return cb(x, y, str) })
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb(objVal, y, str)
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
153
parse.go
Normal file
153
parse.go
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/jsrest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeVal struct {
|
||||||
|
time time.Time
|
||||||
|
precision time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnsupportedType = errors.New("unsupported type")
|
||||||
|
ErrUnknownTimeFormat = errors.New("unknown time format")
|
||||||
|
)
|
||||||
|
|
||||||
|
func parse(str string, t any) (any, error) {
|
||||||
|
typ := reflect.TypeOf(t)
|
||||||
|
|
||||||
|
if typ.Kind() == reflect.Slice {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.Kind() == reflect.Pointer {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider attempting to convert to string in default case
|
||||||
|
switch typ.Kind() { //nolint:exhaustive
|
||||||
|
case reflect.Int:
|
||||||
|
return parseInt(str)
|
||||||
|
|
||||||
|
case reflect.Int64:
|
||||||
|
return strconv.ParseInt(str, 10, 64)
|
||||||
|
|
||||||
|
case reflect.Uint:
|
||||||
|
return parseUint(str)
|
||||||
|
|
||||||
|
case reflect.Uint64:
|
||||||
|
return strconv.ParseUint(str, 10, 64)
|
||||||
|
|
||||||
|
case reflect.Float32:
|
||||||
|
return parseFloat32(str)
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
return strconv.ParseFloat(str, 64)
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
return str, nil
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
return strconv.ParseBool(str)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
switch typ {
|
||||||
|
case reflect.TypeOf(time.Time{}):
|
||||||
|
return parseTime(str)
|
||||||
|
|
||||||
|
case reflect.TypeOf(civil.Date{}):
|
||||||
|
return civil.ParseDate(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, jsrest.Errorf(jsrest.ErrBadRequest, "%T (%w)", t, ErrUnsupportedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInt(str string) (int, error) {
|
||||||
|
val, err := strconv.ParseInt(str, 10, strconv.IntSize)
|
||||||
|
|
||||||
|
return int(val), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint(str string) (uint, error) {
|
||||||
|
val, err := strconv.ParseUint(str, 10, strconv.IntSize)
|
||||||
|
|
||||||
|
return uint(val), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseFloat32(str string) (float32, error) {
|
||||||
|
val, err := strconv.ParseFloat(str, 32)
|
||||||
|
|
||||||
|
return float32(val), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeFormat struct {
|
||||||
|
format string
|
||||||
|
precision time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeFormats = []timeFormat{
|
||||||
|
{
|
||||||
|
format: "2006-01-02-07:00",
|
||||||
|
precision: 24 * time.Hour,
|
||||||
|
// TODO: Support field annotation to change start vs end of day
|
||||||
|
// TODO: Support timezone context passed down to allow naked date
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "2006-01-02T15:04:05Z",
|
||||||
|
precision: 1 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "2006-01-02T15:04:05-07:00",
|
||||||
|
precision: 1 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTime(str string) (*timeVal, error) {
|
||||||
|
if strings.ToLower(str) == "now" {
|
||||||
|
return &timeVal{
|
||||||
|
time: time.Now(),
|
||||||
|
precision: 1 * time.Nanosecond,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, format := range timeFormats {
|
||||||
|
tm, err := time.Parse(format.format, str)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return &timeVal{
|
||||||
|
time: tm,
|
||||||
|
precision: format.precision,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, jsrest.Errorf(jsrest.ErrBadRequest, "%s (%w)", str, ErrUnknownTimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UNIX Seconds: 2969-05-03
|
||||||
|
// UNIX Millis: 1971-01-01
|
||||||
|
// Intended to give us a wide range of useful values in both schemes
|
||||||
|
if i > 31536000000 {
|
||||||
|
return &timeVal{
|
||||||
|
time: time.UnixMilli(i),
|
||||||
|
precision: 1 * time.Millisecond,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &timeVal{
|
||||||
|
time: time.Unix(i, 0),
|
||||||
|
precision: 1 * time.Second,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
42
parse_test.go
Normal file
42
parse_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//nolint:testpackage
|
||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseTimeNow(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
now, err := parse("now", time.Time{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
end := time.Now()
|
||||||
|
|
||||||
|
require.WithinRange(t, now.(*timeVal).time, start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTimeDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
parsed, err := parse("2022-11-01-08:00", time.Time{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tv := parsed.(*timeVal)
|
||||||
|
require.Equal(t, tv.precision, 24*time.Hour)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseTimeSecond(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
parsed, err := parse("2022-11-01T05:06:07Z", time.Time{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tv := parsed.(*timeVal)
|
||||||
|
require.Equal(t, tv.precision, 1*time.Second)
|
||||||
|
}
|
||||||
259
path.go
Normal file
259
path.go
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/jsrest"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WalkCallback func(string, []string, reflect.StructField)
|
||||||
|
|
||||||
|
var (
|
||||||
|
TimeTimeType = reflect.TypeOf(time.Time{})
|
||||||
|
CivilDateType = reflect.TypeOf(civil.Date{})
|
||||||
|
|
||||||
|
ErrNotAStruct = errors.New("not a struct")
|
||||||
|
ErrUnknownFieldName = errors.New("unknown field name")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Get(obj any, path string) (any, error) {
|
||||||
|
v, err := GetValue(reflect.ValueOf(obj), path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetValue(v reflect.Value, path string) (reflect.Value, error) {
|
||||||
|
parts := strings.Split(path, ".")
|
||||||
|
return getRecursive(v, parts, []string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRecursive(v reflect.Value, parts []string, prev []string) (reflect.Value, error) {
|
||||||
|
if v.Kind() == reflect.Pointer {
|
||||||
|
if v.IsNil() {
|
||||||
|
v = reflect.Zero(v.Type().Elem())
|
||||||
|
} else {
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return reflect.Value{}, jsrest.Errorf(jsrest.ErrBadRequest, "%s (%w)", strings.Join(prev, "."), ErrNotAStruct)
|
||||||
|
}
|
||||||
|
|
||||||
|
part := parts[0]
|
||||||
|
|
||||||
|
sub, found := getField(v, part)
|
||||||
|
if !found {
|
||||||
|
return reflect.Value{}, jsrest.Errorf(jsrest.ErrBadRequest, "%s (%w)", errorPath(prev, part), ErrUnknownFieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPrev := []string{}
|
||||||
|
newPrev = append(newPrev, prev...)
|
||||||
|
newPrev = append(newPrev, part)
|
||||||
|
|
||||||
|
return getRecursive(sub, parts[1:], newPrev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getField(v reflect.Value, name string) (reflect.Value, bool) {
|
||||||
|
field, found := getStructField(v.Type(), name)
|
||||||
|
if !found {
|
||||||
|
return reflect.Value{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.FieldByName(field.Name), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Set(obj any, path string, val string) error {
|
||||||
|
return SetValue(reflect.ValueOf(obj), path, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetValue(v reflect.Value, path string, val string) error {
|
||||||
|
parts := strings.Split(path, ".")
|
||||||
|
return setRecursive(v, parts, []string{}, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setRecursive(v reflect.Value, parts []string, prev []string, val string) error {
|
||||||
|
if v.Kind() == reflect.Pointer {
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
v = reflect.Indirect(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 0 {
|
||||||
|
n, err := parse(val, v.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := n.(*timeVal); ok {
|
||||||
|
n = n.(*timeVal).time
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(reflect.ValueOf(n))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Kind() != reflect.Struct {
|
||||||
|
return jsrest.Errorf(jsrest.ErrBadRequest, "%s (%w)", strings.Join(prev, "."), ErrNotAStruct)
|
||||||
|
}
|
||||||
|
|
||||||
|
part := parts[0]
|
||||||
|
|
||||||
|
sub, found := getField(v, part)
|
||||||
|
if !found {
|
||||||
|
return jsrest.Errorf(jsrest.ErrBadRequest, "%s (%w)", errorPath(prev, part), ErrUnknownFieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPrev := []string{}
|
||||||
|
newPrev = append(newPrev, prev...)
|
||||||
|
newPrev = append(newPrev, part)
|
||||||
|
|
||||||
|
return setRecursive(sub, parts[1:], newPrev, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func List(obj any) []string {
|
||||||
|
return ListType(reflect.TypeOf(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListType(t reflect.Type) []string {
|
||||||
|
list := []string{}
|
||||||
|
|
||||||
|
WalkType(t, func(path string, _ []string, field reflect.StructField) {
|
||||||
|
t := MaybeIndirectType(field.Type)
|
||||||
|
if t.Kind() == reflect.Struct && t != TimeTimeType && t != CivilDateType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, path)
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Strings(list)
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFieldType(t reflect.Type, path string) reflect.Type {
|
||||||
|
parts := strings.Split(path, ".")
|
||||||
|
|
||||||
|
for _, part := range parts {
|
||||||
|
field, found := getStructField(MaybeIndirectType(t), part)
|
||||||
|
if !found {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t = field.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindTagValueType(t reflect.Type, key, value string) (string, bool) {
|
||||||
|
ret := ""
|
||||||
|
|
||||||
|
WalkType(t, func(path string, _ []string, field reflect.StructField) {
|
||||||
|
tag, found := field.Tag.Lookup(key)
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(tag, ",")
|
||||||
|
|
||||||
|
if slices.Contains(parts, value) {
|
||||||
|
ret = path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ret, ret != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func Walk(obj any, cb WalkCallback) {
|
||||||
|
WalkType(reflect.TypeOf(obj), cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WalkType(t reflect.Type, cb WalkCallback) {
|
||||||
|
walkRecursive(MaybeIndirectType(t), cb, []string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func walkRecursive(t reflect.Type, cb WalkCallback, prev []string) {
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
sub := t.Field(i)
|
||||||
|
|
||||||
|
newPrev := []string{}
|
||||||
|
newPrev = append(newPrev, prev...)
|
||||||
|
|
||||||
|
if !sub.Anonymous {
|
||||||
|
newPrev = append(newPrev, FieldName(sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
t := MaybeIndirectType(sub.Type)
|
||||||
|
|
||||||
|
if len(newPrev) > 0 {
|
||||||
|
cb(strings.Join(newPrev, "."), newPrev, sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Kind() == reflect.Struct && t != TimeTimeType && t != CivilDateType {
|
||||||
|
walkRecursive(t, cb, newPrev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStructField(t reflect.Type, name string) (reflect.StructField, bool) {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
return t.FieldByNameFunc(func(iterName string) bool {
|
||||||
|
iterField, iterOK := t.FieldByName(iterName)
|
||||||
|
if !iterOK {
|
||||||
|
panic(iterName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.ToLower(FieldName(iterField)) == name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorPath(prev []string, part string) string {
|
||||||
|
if len(prev) == 0 {
|
||||||
|
return part
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s.%s", strings.Join(prev, "."), part)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FieldName(field reflect.StructField) string {
|
||||||
|
tag := field.Tag.Get("json")
|
||||||
|
if tag != "" {
|
||||||
|
if tag == "-" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.SplitN(tag, ",", 2)
|
||||||
|
|
||||||
|
return parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func MaybeIndirectType(t reflect.Type) reflect.Type {
|
||||||
|
if t.Kind() == reflect.Pointer {
|
||||||
|
return t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
141
path_test.go
Normal file
141
path_test.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testType1 struct {
|
||||||
|
Int int
|
||||||
|
Int64 int64
|
||||||
|
UInt uint
|
||||||
|
UInt64 uint64
|
||||||
|
Float32 float32
|
||||||
|
Float64 float64
|
||||||
|
String string `json:"string2,omitempty"`
|
||||||
|
Bool bool `json:"bool2"`
|
||||||
|
BoolP *bool
|
||||||
|
|
||||||
|
Ints []int
|
||||||
|
Int64s []int64
|
||||||
|
UInts []uint
|
||||||
|
UInt64s []uint64
|
||||||
|
Float32s []float32
|
||||||
|
Float64s []float64
|
||||||
|
Strings []string
|
||||||
|
Bools []bool
|
||||||
|
|
||||||
|
Time time.Time
|
||||||
|
Times []time.Time
|
||||||
|
Date civil.Date
|
||||||
|
Dates []civil.Date
|
||||||
|
|
||||||
|
TimeP *time.Time
|
||||||
|
TimesP []*time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type testType2 struct {
|
||||||
|
Tt1 testType1
|
||||||
|
Tt1p *testType1
|
||||||
|
}
|
||||||
|
|
||||||
|
type testType3 struct {
|
||||||
|
testType1
|
||||||
|
}
|
||||||
|
|
||||||
|
type testType4 struct {
|
||||||
|
Foo *testType5 `json:"foo"`
|
||||||
|
testType5
|
||||||
|
}
|
||||||
|
|
||||||
|
type testType5 struct {
|
||||||
|
String string `json:"string2,omitempty"`
|
||||||
|
Bool bool `json:"bool2"`
|
||||||
|
UInt uint `xtest:"foo,bar,zig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSet(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tt1 := &testType1{}
|
||||||
|
err := path.Set(tt1, "int64", "1234")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(1234), tt1.Int64)
|
||||||
|
|
||||||
|
get, err := path.Get(tt1, "int64")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(1234), get)
|
||||||
|
|
||||||
|
err = path.Set(tt1, "time", "2022-11-01-08:00")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int64(1667289600), tt1.Time.Unix())
|
||||||
|
|
||||||
|
tt2 := &testType2{}
|
||||||
|
err = path.Set(tt2, "tt1p.bool2", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, tt2.Tt1p.Bool)
|
||||||
|
|
||||||
|
err = path.Set(tt2, "tt1p.string2", "foo")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "foo", tt2.Tt1p.String)
|
||||||
|
|
||||||
|
err = path.Set(tt2, "tt1.boolp", "true")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, true, *tt2.Tt1.BoolP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmbed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tt3 := &testType3{}
|
||||||
|
err := path.Set(tt3, "int", "1234")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1234, tt3.Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
list := path.List(&testType4{})
|
||||||
|
require.Equal(t, []string{
|
||||||
|
"UInt",
|
||||||
|
"bool2",
|
||||||
|
"foo.UInt",
|
||||||
|
"foo.bool2",
|
||||||
|
"foo.string2",
|
||||||
|
"string2",
|
||||||
|
}, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFieldType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(&testType4{})
|
||||||
|
|
||||||
|
typ2 := path.GetFieldType(typ, "bool2")
|
||||||
|
require.NotNil(t, typ2)
|
||||||
|
require.Equal(t, reflect.TypeOf(true), typ2)
|
||||||
|
|
||||||
|
typ2 = path.GetFieldType(typ, "foo.UInt")
|
||||||
|
require.NotNil(t, typ2)
|
||||||
|
require.Equal(t, reflect.TypeOf(uint(1)), typ2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFindTagValueType(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(&testType4{})
|
||||||
|
|
||||||
|
p, ok := path.FindTagValueType(typ, "xtest", "foo")
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, "UInt", p)
|
||||||
|
|
||||||
|
p, ok = path.FindTagValueType(typ, "xtest", "zag")
|
||||||
|
require.False(t, ok)
|
||||||
|
require.Empty(t, p)
|
||||||
|
}
|
||||||
11
pkg_test.go
Normal file
11
pkg_test.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/goleak"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
goleak.VerifyTestMain(m)
|
||||||
|
}
|
||||||
27
slice.go
Normal file
27
slice.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
func isSlice(v any) bool {
|
||||||
|
return reflect.TypeOf(v).Kind() == reflect.Slice
|
||||||
|
}
|
||||||
|
|
||||||
|
func anyTrue(v any, cb func(any, int) bool) bool {
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
|
||||||
|
for i := 0; i < val.Len(); i++ {
|
||||||
|
sub := val.Index(i)
|
||||||
|
|
||||||
|
if sub.Kind() == reflect.Pointer && sub.IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sub = reflect.Indirect(sub)
|
||||||
|
|
||||||
|
if cb(sub.Interface(), i) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
110
sort.go
Normal file
110
sort.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package path
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/jsrest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sort(objs any, path string) error {
|
||||||
|
as := newAnySlice(objs, path)
|
||||||
|
sort.Stable(as)
|
||||||
|
|
||||||
|
return as.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortReverse(objs any, path string) error {
|
||||||
|
as := newAnySlice(objs, path)
|
||||||
|
sort.Stable(sort.Reverse(as))
|
||||||
|
|
||||||
|
return as.err
|
||||||
|
}
|
||||||
|
|
||||||
|
type anySlice struct {
|
||||||
|
path string
|
||||||
|
slice reflect.Value
|
||||||
|
swapper func(i, j int)
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrUnsupportedSortType = errors.New("unsupported _sort type")
|
||||||
|
|
||||||
|
func newAnySlice(objs any, path string) *anySlice {
|
||||||
|
return &anySlice{
|
||||||
|
path: path,
|
||||||
|
slice: reflect.ValueOf(objs),
|
||||||
|
swapper: reflect.Swapper(objs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *anySlice) Len() int {
|
||||||
|
return as.slice.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *anySlice) Less(i, j int) bool {
|
||||||
|
v1, err := Get(as.slice.Index(i).Interface(), as.path)
|
||||||
|
if err != nil {
|
||||||
|
as.err = err
|
||||||
|
// We have to obey the Less() contract even in error cases
|
||||||
|
return i < j
|
||||||
|
}
|
||||||
|
|
||||||
|
v2, err := Get(as.slice.Index(j).Interface(), as.path)
|
||||||
|
if err != nil {
|
||||||
|
as.err = err
|
||||||
|
return i < j
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case v1 == nil && v2 == nil:
|
||||||
|
return false
|
||||||
|
case v1 == nil:
|
||||||
|
return true
|
||||||
|
case v2 == nil:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t1 := v1.(type) {
|
||||||
|
case int:
|
||||||
|
return t1 < v2.(int)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return t1 < v2.(int64)
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return t1 < v2.(uint)
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return t1 < v2.(uint64)
|
||||||
|
|
||||||
|
case float32:
|
||||||
|
return t1 < v2.(float32)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
return t1 < v2.(float64)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return t1 < v2.(string)
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
return !t1 && v2.(bool)
|
||||||
|
|
||||||
|
case time.Time:
|
||||||
|
return t1.Before(v2.(time.Time))
|
||||||
|
|
||||||
|
case civil.Date:
|
||||||
|
return t1.Before(v2.(civil.Date))
|
||||||
|
|
||||||
|
default:
|
||||||
|
as.err = jsrest.Errorf(jsrest.ErrBadRequest, "%s: %T (%w)", as.path, t1, ErrUnsupportedSortType)
|
||||||
|
return i < j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *anySlice) Swap(i, j int) {
|
||||||
|
as.swapper(i, j)
|
||||||
|
}
|
||||||
282
sort_test.go
Normal file
282
sort_test.go
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
package path_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/civil"
|
||||||
|
"github.com/gopatchy/path"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSortStruct(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType2{
|
||||||
|
{
|
||||||
|
Tt1: testType1{
|
||||||
|
Int: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tt1: testType1{
|
||||||
|
Int: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tt1: testType1{
|
||||||
|
Int: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "tt1.int")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []int{1, 2, 3}, []int{objs[0].Tt1.Int, objs[1].Tt1.Int, objs[2].Tt1.Int})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortReverse(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Int: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.SortReverse(objs, "int")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []int{3, 2, 1}, []int{objs[0].Int, objs[1].Int, objs[2].Int})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortInt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Int: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "int")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []int{1, 2, 3}, []int{objs[0].Int, objs[1].Int, objs[2].Int})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Int64: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int64: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Int64: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "int64")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []int64{1, 2, 3}, []int64{objs[0].Int64, objs[1].Int64, objs[2].Int64})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortUint(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
UInt: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UInt: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UInt: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "uint")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []uint{1, 2, 3}, []uint{objs[0].UInt, objs[1].UInt, objs[2].UInt})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortUint64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
UInt64: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UInt64: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UInt64: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "uint64")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []uint64{1, 2, 3}, []uint64{objs[0].UInt64, objs[1].UInt64, objs[2].UInt64})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortFloat32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Float32: 3.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Float32: 1.1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Float32: 2.2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "float32")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []float32{1.1, 2.2, 3.3}, []float32{objs[0].Float32, objs[1].Float32, objs[2].Float32})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortFloat64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Float64: 3.3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Float64: 1.1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Float64: 2.2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "float64")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []float64{1.1, 2.2, 3.3}, []float64{objs[0].Float64, objs[1].Float64, objs[2].Float64})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
String: "zig",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
String: "foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "string2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []string{"bar", "foo", "zig"}, []string{objs[0].String, objs[1].String, objs[2].String})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortBool(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Bool: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Bool: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Bool: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := path.Sort(objs, "bool2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []bool{false, true, true}, []bool{objs[0].Bool, objs[1].Bool, objs[2].Bool})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortTime(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t1, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-01T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t2, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-02T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t3, err := time.Parse("2006-01-02T15:04:05Z", "2006-01-03T15:04:05Z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Time: t3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Time: t1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Time: t2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = path.Sort(objs, "time")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []time.Time{t1, t2, t3}, []time.Time{objs[0].Time, objs[1].Time, objs[2].Time})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortDate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
d1, err := civil.ParseDate("2006-01-01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
d2, err := civil.ParseDate("2006-01-02")
|
||||||
|
require.NoError(t, err)
|
||||||
|
d3, err := civil.ParseDate("2006-01-03")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
objs := []*testType1{
|
||||||
|
{
|
||||||
|
Date: d3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Date: d1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Date: d2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = path.Sort(objs, "date")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, objs, 3)
|
||||||
|
require.Equal(t, []civil.Date{d1, d2, d3}, []civil.Date{objs[0].Date, objs[1].Date, objs[2].Date})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user