From 0d4692801cdb42e52f03708110b64d4bc14a1b6d Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Wed, 3 Nov 2021 17:30:40 +0000 Subject: [PATCH] Add type selector --- condition_equal.go | 29 ++++++----- condition_more_than.go | 53 +++++++++---------- condition_test.go | 10 ---- internal/command/root_select_test.go | 38 ++++++++++++++ node_query.go | 33 ++++++++++++ node_query_internal_test.go | 77 ++++++++++++++++++---------- node_query_multiple.go | 57 ++++++++++++++++++++ node_query_multiple_internal_test.go | 70 +++++++++++++++---------- parse_selector.go | 7 +++ 9 files changed, 269 insertions(+), 105 deletions(-) diff --git a/condition_equal.go b/condition_equal.go index 7b0c4c3..d38d058 100644 --- a/condition_equal.go +++ b/condition_equal.go @@ -36,21 +36,22 @@ func (c EqualCondition) Check(other reflect.Value) (bool, error) { return c.check(value.Interface(), c.Value) } - switch value.Kind() { - case reflect.Map, reflect.Slice: - subRootNode := New(value.Interface()) - foundNode, err := subRootNode.Query(c.Key) - if err != nil { - var valueNotFound = &ValueNotFound{} - if errors.As(err, &valueNotFound) { - return false, nil - } - - return false, fmt.Errorf("subquery failed: %w", err) + fmt.Println("here456") + subRootNode := New(value.Interface()) + foundNode, err := subRootNode.Query(c.Key) + fmt.Println("789", err) + if err != nil { + fmt.Println("here123") + var valueNotFound = &ValueNotFound{} + if errors.As(err, &valueNotFound) { + return false, nil + } + var unsupportedType = &UnsupportedTypeForSelector{} + if errors.As(err, &unsupportedType) { + return false, nil } - return c.check(foundNode.InterfaceValue(), c.Value) + return false, fmt.Errorf("subquery failed: %w", err) } - - return false, &UnhandledCheckType{Value: value.String()} + return c.check(foundNode.InterfaceValue(), c.Value) } diff --git a/condition_more_than.go b/condition_more_than.go index 22c2ba6..c35429d 100644 --- a/condition_more_than.go +++ b/condition_more_than.go @@ -31,37 +31,32 @@ func (c SortedComparisonCondition) Check(other reflect.Value) (bool, error) { return fmt.Sprint(value.Interface()) == c.Value, nil } - switch value.Kind() { - case reflect.Map, reflect.Slice: - subRootNode := New(value.Interface()) - foundNode, err := subRootNode.Query(c.Key) - if err != nil { - var valueNotFound = &ValueNotFound{} - if errors.As(err, &valueNotFound) { - return false, nil - } - - return false, fmt.Errorf("subquery failed: %w", err) + subRootNode := New(value.Interface()) + foundNode, err := subRootNode.Query(c.Key) + if err != nil { + var valueNotFound = &ValueNotFound{} + if errors.As(err, &valueNotFound) { + return false, nil } - foundValueStr := fmt.Sprint(foundNode.InterfaceValue()) - - // Check if the values are equal - if foundValueStr == c.Value { - return c.Equal, nil - } - - sortedVals := []string{foundValueStr, c.Value} - sort.Strings(sortedVals) - - if !c.After && sortedVals[1] == c.Value { - return true, nil - } else if c.After && sortedVals[0] == c.Value { - return true, nil - } - - return false, nil + return false, fmt.Errorf("subquery failed: %w", err) } - return false, &UnhandledCheckType{Value: value.String()} + foundValueStr := fmt.Sprint(foundNode.InterfaceValue()) + + // Check if the values are equal + if foundValueStr == c.Value { + return c.Equal, nil + } + + sortedVals := []string{foundValueStr, c.Value} + sort.Strings(sortedVals) + + if !c.After && sortedVals[1] == c.Value { + return true, nil + } else if c.After && sortedVals[0] == c.Value { + return true, nil + } + + return false, nil } diff --git a/condition_test.go b/condition_test.go index eebcbf0..99cf2fc 100644 --- a/condition_test.go +++ b/condition_test.go @@ -67,11 +67,6 @@ func TestEqualCondition_Check(t *testing.T) { nil, false, &dasel.UnhandledCheckType{Value: nil}, )) - t.Run("String", conditionTest( - c, - "", - false, &dasel.UnhandledCheckType{Value: ""}, - )) } func TestSortedComparisonCondition_Check(t *testing.T) { @@ -163,11 +158,6 @@ func TestSortedComparisonCondition_Check(t *testing.T) { nil, false, &dasel.UnhandledCheckType{Value: nil}, )) - t.Run("String", conditionTest( - &dasel.SortedComparisonCondition{Key: "x", Value: "4"}, - "", - false, &dasel.UnhandledCheckType{Value: ""}, - )) } func TestKeyEqualCondition_Check(t *testing.T) { diff --git a/internal/command/root_select_test.go b/internal/command/root_select_test.go index 2b7f769..a37e910 100644 --- a/internal/command/root_select_test.go +++ b/internal/command/root_select_test.go @@ -493,6 +493,44 @@ func TestRootCmd_Select_JSON(t *testing.T) { } `, nil, "--escape-html=false")) + t.Run("MixedDynamicSelectors", selectTest(`{ + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/gitlab", + [ + "@semantic-release/git", + { + "assets": [ + "tbump.toml", + "**/pyproject.toml", + "**/setup.py", + "README.md" + ], + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "y" + ], + "message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}" + } + ] + ] +}`, "json", `.plugins.([@]=array).([@]=map).assets`, `[ + "tbump.toml", + "**/pyproject.toml", + "**/setup.py", + "README.md" +] +[ + "y" +] +`, nil, "-m")) + } func TestRootCmd_Select_YAML(t *testing.T) { diff --git a/node_query.go b/node_query.go index 0b60e1b..94fbc11 100644 --- a/node_query.go +++ b/node_query.go @@ -218,6 +218,37 @@ func findValueLength(n *Node, createIfNotExists bool) (reflect.Value, error) { return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value} } +// findValueType returns the type of the current node. +func findValueType(n *Node, createIfNotExists bool) (reflect.Value, error) { + if !isValid(n.Previous.Value) { + return nilValue(), &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current} + } + + value := unwrapValue(n.Previous.Value) + + switch value.Kind() { + case reflect.Slice: + return reflect.ValueOf("array"), nil + + case reflect.Map: + return reflect.ValueOf("map"), nil + + case reflect.String: + return reflect.ValueOf("string"), nil + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return reflect.ValueOf("int"), nil + + case reflect.Float32, reflect.Float64: + return reflect.ValueOf("float"), nil + + case reflect.Bool: + return reflect.ValueOf("bool"), nil + } + + return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value} +} + // findValue finds the value for the given node. // The value is essentially pulled from the previous node, using the (already parsed) selector // information stored on the current node. @@ -242,6 +273,8 @@ func findValue(n *Node, createIfNotExists bool) (reflect.Value, error) { return findValueDynamic(n, createIfNotExists) case "LENGTH": return findValueLength(n, createIfNotExists) + case "TYPE": + return findValueType(n, createIfNotExists) default: return nilValue(), &UnsupportedSelector{Selector: n.Selector.Raw} } diff --git a/node_query_internal_test.go b/node_query_internal_test.go index 2542a79..aaf9403 100644 --- a/node_query_internal_test.go +++ b/node_query_internal_test.go @@ -145,32 +145,6 @@ func TestFindValueDynamic(t *testing.T) { return } }) - t.Run("UnsupportedCheckType", func(t *testing.T) { - itemVal := 1 - val := []interface{}{ - itemVal, - } - n := getNodeWithValue(val) - n.Selector.Current = "(name=x)" - n.Selector.Conditions = []Condition{ - &EqualCondition{Key: "name", Value: "x"}, - } - got, err := findValueDynamic(n, false) - assertQueryResult(t, nilValue(), &UnhandledCheckType{Value: reflect.TypeOf(itemVal).Kind().String()}, got, err) - }) - t.Run("UnsupportedCheckTypeMap", func(t *testing.T) { - itemVal := 1 - val := map[string]interface{}{ - "x": itemVal, - } - n := getNodeWithValue(val) - n.Selector.Current = "(name=x)" - n.Selector.Conditions = []Condition{ - &EqualCondition{Key: "name", Value: "x"}, - } - got, err := findValueDynamic(n, false) - assertQueryResult(t, nilValue(), &UnhandledCheckType{Value: reflect.TypeOf(itemVal).Kind().String()}, got, err) - }) t.Run("UnsupportedType", func(t *testing.T) { val := 0 n := getNodeWithValue(val) @@ -227,6 +201,57 @@ func TestFindValueLength(t *testing.T) { }) } +func TestFindValueType(t *testing.T) { + t.Run("NilValue", func(t *testing.T) { + n := getNodeWithValue(nil) + n.Previous.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, nilValue(), &UnexpectedPreviousNilValue{Selector: ".[#]"}, got, err) + }) + t.Run("Int", func(t *testing.T) { + val := 0 + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("int"), nil, got, err) + }) + t.Run("Float", func(t *testing.T) { + val := 1.1 + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("float"), nil, got, err) + }) + t.Run("Bool", func(t *testing.T) { + val := true + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("bool"), nil, got, err) + }) + t.Run("String", func(t *testing.T) { + val := "x" + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("string"), nil, got, err) + }) + t.Run("Map", func(t *testing.T) { + val := map[string]interface{}{"x": 1} + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("map"), nil, got, err) + }) + t.Run("Array", func(t *testing.T) { + val := []interface{}{1} + n := getNodeWithValue(val) + n.Selector.Current = ".[#]" + got, err := findValueType(n, false) + assertQueryResult(t, reflect.ValueOf("array"), nil, got, err) + }) +} + func TestFindValue(t *testing.T) { t.Run("MissingPreviousNode", func(t *testing.T) { n := New(nil) diff --git a/node_query_multiple.go b/node_query_multiple.go index 02b8d78..da2d7a8 100644 --- a/node_query_multiple.go +++ b/node_query_multiple.go @@ -459,6 +459,61 @@ func findNodesLength(selector Selector, previousValue reflect.Value) ([]*Node, e return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value} } +// findNodesType returns the length +func findNodesType(selector Selector, previousValue reflect.Value) ([]*Node, error) { + if !isValid(previousValue) { + return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw} + } + + value := unwrapValue(previousValue) + + switch value.Kind() { + case reflect.Slice: + node := &Node{ + Value: reflect.ValueOf("array"), + Selector: selector, + } + return []*Node{node}, nil + + case reflect.Map: + node := &Node{ + Value: reflect.ValueOf("map"), + Selector: selector, + } + return []*Node{node}, nil + + case reflect.String: + node := &Node{ + Value: reflect.ValueOf("string"), + Selector: selector, + } + return []*Node{node}, nil + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + node := &Node{ + Value: reflect.ValueOf("int"), + Selector: selector, + } + return []*Node{node}, nil + + case reflect.Float32, reflect.Float64: + node := &Node{ + Value: reflect.ValueOf("float"), + Selector: selector, + } + return []*Node{node}, nil + + case reflect.Bool: + node := &Node{ + Value: reflect.ValueOf("bool"), + Selector: selector, + } + return []*Node{node}, nil + } + + return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value} +} + func initialiseEmptyValue(selector Selector, previousValue reflect.Value) reflect.Value { switch selector.Type { case "PROPERTY": @@ -489,6 +544,8 @@ func findNodes(selector Selector, previousNode *Node, createIfNotExists bool) ([ res, err = findNodesAnyIndex(selector, previousNode.Value) case "LENGTH": res, err = findNodesLength(selector, previousNode.Value) + case "TYPE": + res, err = findNodesType(selector, previousNode.Value) case "DYNAMIC": res, err = findNodesDynamic(selector, previousNode.Value, createIfNotExists) case "SEARCH": diff --git a/node_query_multiple_internal_test.go b/node_query_multiple_internal_test.go index fa52774..b937108 100644 --- a/node_query_multiple_internal_test.go +++ b/node_query_multiple_internal_test.go @@ -131,6 +131,50 @@ func TestFindNodesLength(t *testing.T) { }) } +func TestFindNodesType(t *testing.T) { + t.Run("NilValue", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + got, err := findNodesType(selector, nilValue()) + assertQueryMultipleResult(t, []reflect.Value{}, &UnexpectedPreviousNilValue{Selector: ".[#]"}, got, err) + }) + t.Run("Int", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := 0 + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("int")}, nil, got, err) + }) + t.Run("Float", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := 1.1 + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("float")}, nil, got, err) + }) + t.Run("Bool", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := true + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("bool")}, nil, got, err) + }) + t.Run("String", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := "a" + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("string")}, nil, got, err) + }) + t.Run("Map", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := map[string]interface{}{"x": 1} + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("map")}, nil, got, err) + }) + t.Run("Array", func(t *testing.T) { + selector := Selector{Current: ".[#]", Raw: ".[#]"} + val := []interface{}{"x"} + got, err := findNodesType(selector, reflect.ValueOf(val)) + assertQueryMultipleResult(t, []reflect.Value{reflect.ValueOf("array")}, nil, got, err) + }) +} + func TestFindNodesPropertyKeys(t *testing.T) { t.Run("NilValue", func(t *testing.T) { selector := Selector{Current: ".", Raw: "."} @@ -279,32 +323,6 @@ func TestFindNodesDynamic(t *testing.T) { return } }) - t.Run("UnsupportedCheckType", func(t *testing.T) { - previousValue := reflect.ValueOf([]interface{}{ - 1, - }) - selector := Selector{ - Current: "(name=x)", - Conditions: []Condition{ - &EqualCondition{Key: "name", Value: "x"}, - }, - } - got, err := findNodesDynamic(selector, previousValue, false) - assertQueryMultipleResult(t, []reflect.Value{}, &UnhandledCheckType{Value: previousValue.Kind().String()}, got, err) - }) - t.Run("UnsupportedCheckTypeMap", func(t *testing.T) { - previousValue := reflect.ValueOf(map[string]interface{}{ - "x": 1, - }) - selector := Selector{ - Current: "(name=x)", - Conditions: []Condition{ - &EqualCondition{Key: "name", Value: "x"}, - }, - } - got, err := findNodesDynamic(selector, previousValue, false) - assertQueryMultipleResult(t, []reflect.Value{}, &UnhandledCheckType{Value: previousValue.Kind().String()}, got, err) - }) t.Run("UnsupportedType", func(t *testing.T) { previousValue := reflect.ValueOf(0) selector := Selector{ diff --git a/parse_selector.go b/parse_selector.go index 7196031..2e3d032 100644 --- a/parse_selector.go +++ b/parse_selector.go @@ -37,6 +37,8 @@ func ParseSelector(selector string) (Selector, error) { sel, err = processParseSelectorIndexAny(nextSel, sel) case nextSel == "[#]": sel, err = processParseSelectorLength(nextSel, sel) + case nextSel == "[@]": + sel, err = processParseSelectorType(nextSel, sel) case strings.HasPrefix(nextSel, "[") && strings.HasSuffix(nextSel, "]"): sel, err = processParseSelectorIndex(nextSel, sel) default: @@ -168,6 +170,11 @@ func processParseSelectorLength(selector string, sel Selector) (Selector, error) return sel, nil } +func processParseSelectorType(selector string, sel Selector) (Selector, error) { + sel.Type = "TYPE" + return sel, nil +} + func processParseSelectorIndex(selector string, sel Selector) (Selector, error) { sel.Type = "INDEX" indexStr := selector[1 : len(selector)-1]