mirror of
https://github.com/TomWright/dasel.git
synced 2022-05-22 02:32:45 +03:00
282 lines
7.6 KiB
Go
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}
|
|
}
|
|
}
|