Identify task (#1839)

* Add identify task
* Change type naming
* Debounce folder select text input
* Add generic slice comparison function
This commit is contained in:
WithoutPants
2021-10-28 14:25:17 +11:00
committed by GitHub
parent c93b5e12b7
commit 0f64954e5b
70 changed files with 5882 additions and 291 deletions

60
pkg/utils/collections.go Normal file
View File

@@ -0,0 +1,60 @@
package utils
import "reflect"
// SliceSame returns true if the two provided lists have the same elements,
// regardless of order. Panics if either parameter is not a slice.
func SliceSame(a, b interface{}) bool {
v1 := reflect.ValueOf(a)
v2 := reflect.ValueOf(b)
if (v1.IsValid() && v1.Kind() != reflect.Slice) || (v2.IsValid() && v2.Kind() != reflect.Slice) {
panic("not a slice")
}
v1Len := 0
v2Len := 0
v1Valid := v1.IsValid()
v2Valid := v2.IsValid()
if v1Valid {
v1Len = v1.Len()
}
if v2Valid {
v2Len = v2.Len()
}
if !v1Valid || !v2Valid {
return v1Len == v2Len
}
if v1Len != v2Len {
return false
}
if v1.Type() != v2.Type() {
return false
}
visited := make(map[int]bool)
for i := 0; i < v1.Len(); i++ {
found := false
for j := 0; j < v2.Len(); j++ {
if visited[j] {
continue
}
if reflect.DeepEqual(v1.Index(i).Interface(), v2.Index(j).Interface()) {
found = true
visited[j] = true
break
}
}
if !found {
return false
}
}
return true
}

View File

@@ -0,0 +1,92 @@
package utils
import "testing"
func TestSliceSame(t *testing.T) {
objs := []struct {
a string
b int
}{
{"1", 2},
{"1", 2},
{"2", 1},
}
tests := []struct {
name string
a interface{}
b interface{}
want bool
}{
{"nil values", nil, nil, true},
{"empty", []int{}, []int{}, true},
{"nil and empty", nil, []int{}, true},
{
"different type",
[]string{"1"},
[]int{1},
false,
},
{
"different length",
[]int{1, 2, 3},
[]int{1, 2},
false,
},
{
"equal",
[]int{1, 2, 3, 4, 5},
[]int{1, 2, 3, 4, 5},
true,
},
{
"different order",
[]int{5, 4, 3, 2, 1},
[]int{1, 2, 3, 4, 5},
true,
},
{
"different",
[]int{5, 4, 3, 2, 6},
[]int{1, 2, 3, 4, 5},
false,
},
{
"same with duplicates",
[]int{1, 1, 2, 3, 4},
[]int{1, 2, 3, 4, 1},
true,
},
{
"subset",
[]int{1, 1, 2, 2, 3},
[]int{1, 2, 3, 4, 5},
false,
},
{
"superset",
[]int{1, 2, 3, 4, 5},
[]int{1, 1, 2, 2, 3},
false,
},
{
"structs equal",
objs[0:1],
objs[0:1],
true,
},
{
"structs not equal",
objs[0:2],
objs[1:3],
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := SliceSame(tt.a, tt.b); got != tt.want {
t.Errorf("SliceSame() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,5 +1,7 @@
package utils
import "strconv"
// IntIndex returns the first index of the provided int value in the provided
// int slice. It returns -1 if it is not found.
func IntIndex(vs []int, t int) int {
@@ -50,3 +52,13 @@ func IntExclude(vs []int, toExclude []int) []int {
return ret
}
// IntSliceToStringSlice converts a slice of ints to a slice of strings.
func IntSliceToStringSlice(ss []int) []string {
ret := make([]string, len(ss))
for i, v := range ss {
ret[i] = strconv.Itoa(v)
}
return ret
}

30
pkg/utils/reflect.go Normal file
View File

@@ -0,0 +1,30 @@
package utils
import "reflect"
// NotNilFields returns the matching tag values of fields from an object that are not nil.
// Panics if the provided object is not a struct.
func NotNilFields(subject interface{}, tag string) []string {
value := reflect.ValueOf(subject)
structType := value.Type()
if structType.Kind() != reflect.Struct {
panic("subject must be struct")
}
var ret []string
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
kind := field.Type().Kind()
if (kind == reflect.Ptr || kind == reflect.Slice) && !field.IsNil() {
tagValue := structType.Field(i).Tag.Get(tag)
if tagValue != "" {
ret = append(ret, tagValue)
}
}
}
return ret
}

83
pkg/utils/reflect_test.go Normal file
View File

@@ -0,0 +1,83 @@
package utils
import (
"reflect"
"testing"
)
func TestNotNilFields(t *testing.T) {
v := "value"
var zeroStr string
type testObject struct {
ptrField *string `tag:"ptrField"`
noTagField *string
otherTagField *string `otherTag:"otherTagField"`
sliceField []string `tag:"sliceField"`
}
type args struct {
subject interface{}
tag string
}
tests := []struct {
name string
args args
want []string
}{
{
"basic",
args{
testObject{
ptrField: &v,
noTagField: &v,
otherTagField: &v,
sliceField: []string{v},
},
"tag",
},
[]string{"ptrField", "sliceField"},
},
{
"empty",
args{
testObject{},
"tag",
},
nil,
},
{
"zero values",
args{
testObject{
ptrField: &zeroStr,
noTagField: &zeroStr,
otherTagField: &zeroStr,
sliceField: []string{},
},
"tag",
},
[]string{"ptrField", "sliceField"},
},
{
"other tag",
args{
testObject{
ptrField: &v,
noTagField: &v,
otherTagField: &v,
sliceField: []string{v},
},
"otherTag",
},
[]string{"otherTagField"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NotNilFields(tt.args.subject, tt.args.tag); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NotNilFields() = %v, want %v", got, tt.want)
}
})
}
}