1
0
mirror of https://github.com/TomWright/dasel.git synced 2022-05-22 02:32:45 +03:00
Files
dasel-data-selector/node_query.go
2021-11-03 17:30:40 +00:00

282 lines
7.6 KiB
Go

package dasel
import (
"fmt"
"reflect"
)
// Query uses the given selector to query the current node and return the result.
func (n *Node) Query(selector string) (*Node, error) {
n.Selector.Remaining = selector
rootNode := n
if err := buildFindChain(rootNode); err != nil {
return nil, err
}
return lastNode(rootNode), nil
}
// lastNode returns the last node in the chain.
// If a node contains multiple next nodes, the first node is taken.
func lastNode(n *Node) *Node {
node := n
for {
if node.Next == nil {
return node
}
node = node.Next
}
}
func isFinalSelector(selector string) bool {
return selector == "" || selector == "."
}
func buildFindChain(n *Node) error {
if isFinalSelector(n.Selector.Remaining) {
// We've reached the end
return nil
}
var err error
nextNode := &Node{}
// Parse the selector.
nextNode.Selector, err = ParseSelector(n.Selector.Remaining)
if err != nil {
return fmt.Errorf("failed to parse selector: %w", err)
}
// Link the nodes.
n.Next = nextNode
nextNode.Previous = n
// Populate the value for the new node.
nextNode.Value, err = findValue(nextNode, false)
if err != nil {
return fmt.Errorf("could not find value: %w", err)
}
return buildFindChain(nextNode)
}
// findValueProperty finds the value for the given node using the property selector
// information.
func findValueProperty(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)
if value.Kind() == reflect.Map {
for _, key := range value.MapKeys() {
if fmt.Sprint(key.Interface()) == n.Selector.Property {
return value.MapIndex(key), nil
}
}
if createIfNotExists {
return nilValue(), nil
}
return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
}
return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// findValueIndex finds the value for the given node using the index selector
// information.
func findValueIndex(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)
if value.Kind() == reflect.Slice {
valueLen := value.Len()
if n.Selector.Index >= 0 && n.Selector.Index < valueLen {
return value.Index(n.Selector.Index), nil
}
if createIfNotExists {
return nilValue(), nil
}
return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
}
return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// findNextAvailableIndex finds the value for the given node using the index selector
// information.
func findNextAvailableIndex(n *Node, createIfNotExists bool) (reflect.Value, error) {
if !createIfNotExists {
// Next available index isn't supported unless it's creating the item.
return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
}
return nilValue(), nil
}
// processFindDynamicItem is used by findValueDynamic.
func processFindDynamicItem(n *Node, object reflect.Value, key string) (bool, error) {
// Loop through each condition.
allConditionsMatched := true
for _, c := range n.Selector.Conditions {
// If the object doesn't match any checks, return a ValueNotFound.
var found bool
var err error
switch cond := c.(type) {
case *KeyEqualCondition:
found, err = cond.Check(reflect.ValueOf(key))
default:
found, err = cond.Check(object)
}
if err != nil {
return false, err
}
if !found {
allConditionsMatched = false
break
}
}
if allConditionsMatched {
return true, nil
}
return false, nil
}
// findValueDynamic finds the value for the given node using the dynamic selector
// information.
func findValueDynamic(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:
for i := 0; i < value.Len(); i++ {
object := value.Index(i)
found, err := processFindDynamicItem(n, object, fmt.Sprint(i))
if err != nil {
return nilValue(), err
}
if found {
n.Selector.Type = "INDEX"
n.Selector.Index = i
return object, nil
}
}
if createIfNotExists {
n.Selector.Type = "NEXT_AVAILABLE_INDEX"
return nilValue(), nil
}
return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
case reflect.Map:
for _, key := range value.MapKeys() {
object := value.MapIndex(key)
found, err := processFindDynamicItem(n, object, key.String())
if err != nil {
return nilValue(), err
}
if found {
n.Selector.Type = "PROPERTY"
n.Selector.Property = key.String()
return object, nil
}
}
return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
}
return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// findValueLength returns the length of the current node.
func findValueLength(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(value.Len()), nil
case reflect.Map:
return reflect.ValueOf(value.Len()), nil
case reflect.String:
return reflect.ValueOf(value.Len()), nil
}
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.
func findValue(n *Node, createIfNotExists bool) (reflect.Value, error) {
if n.Previous == nil {
// previous node is required to get it's value.
return nilValue(), ErrMissingPreviousNode
}
if createIfNotExists && !isValid(n.Previous.Value) {
n.Previous.Value = initialiseEmptyValue(n.Selector, n.Previous.Value)
}
switch n.Selector.Type {
case "PROPERTY":
return findValueProperty(n, createIfNotExists)
case "INDEX":
return findValueIndex(n, createIfNotExists)
case "NEXT_AVAILABLE_INDEX":
return findNextAvailableIndex(n, createIfNotExists)
case "DYNAMIC":
return findValueDynamic(n, createIfNotExists)
case "LENGTH":
return findValueLength(n, createIfNotExists)
case "TYPE":
return findValueType(n, createIfNotExists)
default:
return nilValue(), &UnsupportedSelector{Selector: n.Selector.Raw}
}
}