Repository: TomWright/dasel
Pull request #224 (2022-06-12 21:40:02)

Add support for struct types


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3990b70..725ead1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
-- Nothing yet.
+### Added
+
+- Support for struct type usage in go package.
 
 ## [v1.24.3] - 2022-04-23
 
diff --git a/node.go b/node.go
index b185233..7c05b75 100644
--- a/node.go
+++ b/node.go
@@ -106,13 +106,19 @@ func nilValue() reflect.Value {
 }
 
 func unwrapValue(value reflect.Value) reflect.Value {
-	// value = reflect.Indirect(value)
 	if value.Kind() == reflect.Interface {
 		return value.Elem()
 	}
 	return value
 }
 
+func derefValue(value reflect.Value) reflect.Value {
+	if value.Kind() == reflect.Ptr {
+		return value.Elem()
+	}
+	return value
+}
+
 // New returns a new root node with the given value.
 func New(value interface{}) *Node {
 	rootNode := &Node{
diff --git a/node_propagate.go b/node_propagate.go
index 1d455ce..6f67bc0 100644
--- a/node_propagate.go
+++ b/node_propagate.go
@@ -35,23 +35,34 @@ func propagateValue(n *Node) error {
 	}
 }
 
-// propagateValueProperty sends the value of the current node up to the previous node in the chain.
-func propagateValueProperty(n *Node) error {
-	if !isValid(n.Previous.Value) {
-		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
-	}
-
-	value := unwrapValue(n.Previous.Value)
-
-	if value.Kind() == reflect.Map {
+func propagateValuePropertyWork(n *Node, value reflect.Value) error {
+	switch value.Kind() {
+	case reflect.Map:
 		value.SetMapIndex(reflect.ValueOf(n.Selector.Property), n.Value)
 		return nil
+	case reflect.Struct:
+		fieldV := value.FieldByName(n.Selector.Property)
+		if fieldV.IsValid() {
+			fieldV.Set(n.Value)
+		}
+		return nil
+	case reflect.Ptr:
+		return propagateValuePropertyWork(n, derefValue(value))
 	}
 
 	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: n.Previous.Value}
 }
 
+// propagateValueProperty sends the value of the current node up to the previous node in the chain.
+func propagateValueProperty(n *Node) error {
+	if !isValid(n.Previous.Value) {
+		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
+	}
+	return propagateValuePropertyWork(n, unwrapValue(n.Previous.Value))
+}
+
 // propagateValueIndex sends the value of the current node up to the previous node in the chain.
+// No need to support structs here since a struct can't have an index.
 func propagateValueIndex(n *Node) error {
 	if !isValid(n.Previous.Value) {
 		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
@@ -103,22 +114,32 @@ func deleteFromParent(n *Node) error {
 	}
 }
 
-// deleteFromParentProperty sends the value of the current node up to the previous node in the chain.
-func deleteFromParentProperty(n *Node) error {
-	if !isValid(n.Previous.Value) {
-		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
-	}
-
-	value := unwrapValue(n.Previous.Value)
-
-	if value.Kind() == reflect.Map {
+func deleteFromParentPropertyWork(n *Node, value reflect.Value) error {
+	switch value.Kind() {
+	case reflect.Map:
 		value.SetMapIndex(reflect.ValueOf(n.Selector.Property), reflect.Value{})
 		return nil
+	case reflect.Struct:
+		fieldV := value.FieldByName(n.Selector.Property)
+		if fieldV.CanSet() && fieldV.IsValid() && !fieldV.IsZero() {
+			fieldV.Set(reflect.New(fieldV.Type()).Elem())
+		}
+		return nil
+	case reflect.Ptr:
+		return deleteFromParentPropertyWork(n, derefValue(value))
 	}
 
 	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: n.Previous.Value}
 }
 
+// deleteFromParentProperty sends the value of the current node up to the previous node in the chain.
+func deleteFromParentProperty(n *Node) error {
+	if !isValid(n.Previous.Value) {
+		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
+	}
+	return deleteFromParentPropertyWork(n, unwrapValue(n.Previous.Value))
+}
+
 // deleteFromParentIndex sends the value of the current node up to the previous node in the chain.
 func deleteFromParentIndex(n *Node) error {
 	if !isValid(n.Previous.Value) {
@@ -183,6 +204,7 @@ func getDeletePlaceholder(item reflect.Value) reflect.Value {
 }
 
 func isDeletePlaceholder(item reflect.Value) bool {
+	// No need to handle structs since on delete we set the field to a zero value.
 	switch i := unwrapValue(item); i.Kind() {
 	case reflect.Map:
 		if val, ok := i.Interface().(map[string]interface{})[deletePlaceholderKey]; ok {
diff --git a/node_query.go b/node_query.go
index 94fbc11..ae5d6af 100644
--- a/node_query.go
+++ b/node_query.go
@@ -61,16 +61,9 @@ func buildFindChain(n *Node) error {
 	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 {
+func findValuePropertyWork(n *Node, createIfNotExists bool, value reflect.Value) (reflect.Value, error) {
+	switch value.Kind() {
+	case reflect.Map:
 		for _, key := range value.MapKeys() {
 			if fmt.Sprint(key.Interface()) == n.Selector.Property {
 				return value.MapIndex(key), nil
@@ -80,11 +73,30 @@ func findValueProperty(n *Node, createIfNotExists bool) (reflect.Value, error) {
 			return nilValue(), nil
 		}
 		return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
+
+	case reflect.Struct:
+		fieldV := value.FieldByName(n.Selector.Property)
+		if fieldV.IsValid() {
+			return fieldV, nil
+		}
+		return nilValue(), &ValueNotFound{Selector: n.Selector.Current, PreviousValue: n.Previous.Value}
+
+	case reflect.Ptr:
+		return findValuePropertyWork(n, createIfNotExists, derefValue(value))
 	}
 
 	return nilValue(), &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
 }
 
+// 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}
+	}
+	return findValuePropertyWork(n, createIfNotExists, unwrapValue(n.Previous.Value))
+}
+
 // findValueIndex finds the value for the given node using the index selector
 // information.
 func findValueIndex(n *Node, createIfNotExists bool) (reflect.Value, error) {
@@ -196,14 +208,7 @@ func findValueDynamic(n *Node, createIfNotExists bool) (reflect.Value, error) {
 	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)
-
+func findValueLengthWork(n *Node, value reflect.Value) (reflect.Value, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		return reflect.ValueOf(value.Len()), nil
@@ -213,19 +218,26 @@ func findValueLength(n *Node, createIfNotExists bool) (reflect.Value, error) {
 
 	case reflect.String:
 		return reflect.ValueOf(value.Len()), nil
+
+	case reflect.Struct:
+		return reflect.ValueOf(value.NumField()), nil
+
+	case reflect.Ptr:
+		return findValueLengthWork(n, derefValue(value))
 	}
 
 	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) {
+// 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}
 	}
+	return findValueLengthWork(n, unwrapValue(n.Previous.Value))
+}
 
-	value := unwrapValue(n.Previous.Value)
-
+func findValueTypeWork(n *Node, value reflect.Value) (reflect.Value, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		return reflect.ValueOf("array"), nil
@@ -244,11 +256,25 @@ func findValueType(n *Node, createIfNotExists bool) (reflect.Value, error) {
 
 	case reflect.Bool:
 		return reflect.ValueOf("bool"), nil
+
+	case reflect.Struct:
+		return reflect.ValueOf("struct"), nil
+
+	case reflect.Ptr:
+		return findValueTypeWork(n, derefValue(value))
 	}
 
 	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}
+	}
+	return findValueTypeWork(n, unwrapValue(n.Previous.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.
diff --git a/node_query_multiple.go b/node_query_multiple.go
index da2d7a8..57a80d1 100644
--- a/node_query_multiple.go
+++ b/node_query_multiple.go
@@ -64,20 +64,9 @@ func buildFindMultipleChain(n *Node) error {
 	return nil
 }
 
-// findNodesProperty finds the value for the given node using the property selector
-// information.
-func findNodesProperty(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
-	if !isValid(previousValue) {
-		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
-	}
-
-	if selector.Property == "-" {
-		res, err := findNodesPropertyKeys(selector, previousValue, createIfNotExists)
-		return res, err
-	}
-
-	value := unwrapValue(previousValue)
-	if value.Kind() == reflect.Map {
+func findNodesPropertyWork(selector Selector, previousValue reflect.Value, createIfNotExists bool, value reflect.Value) ([]*Node, error) {
+	switch value.Kind() {
+	case reflect.Map:
 		node := &Node{
 			Value:    nilValue(),
 			Selector: selector,
@@ -92,21 +81,42 @@ func findNodesProperty(selector Selector, previousValue reflect.Value, createIfN
 			return []*Node{node}, nil
 		}
 		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Struct:
+		node := &Node{
+			Value:    nilValue(),
+			Selector: selector,
+		}
+		fieldV := value.FieldByName(selector.Property)
+		if fieldV.IsValid() {
+			node.Value = fieldV
+			return []*Node{node}, nil
+		}
+		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Ptr:
+		return findNodesPropertyWork(selector, previousValue, createIfNotExists, derefValue(value))
 	}
 
 	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
 }
 
-func findNodesPropertyKeys(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
+// findNodesProperty finds the value for the given node using the property selector
+// information.
+func findNodesProperty(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
 	if !isValid(previousValue) {
 		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
 	}
-	if createIfNotExists {
-		return nil, &UnsupportedSelector{Selector: selector.Raw}
+
+	if selector.Property == "-" {
+		res, err := findNodesPropertyKeys(selector, previousValue, createIfNotExists)
+		return res, err
 	}
 
-	value := unwrapValue(previousValue)
+	return findNodesPropertyWork(selector, previousValue, createIfNotExists, unwrapValue(previousValue))
+}
 
+func findNodesPropertyKeysWork(selector Selector, previousValue reflect.Value, createIfNotExists bool, value reflect.Value) ([]*Node, error) {
 	results := make([]*Node, 0)
 
 	switch value.Kind() {
@@ -120,6 +130,7 @@ func findNodesPropertyKeys(selector Selector, previousValue reflect.Value, creat
 				Selector: sel,
 			})
 		}
+		return results, nil
 
 	case reflect.Map:
 		for _, key := range value.MapKeys() {
@@ -131,11 +142,37 @@ func findNodesPropertyKeys(selector Selector, previousValue reflect.Value, creat
 				Selector: sel,
 			})
 		}
-	default:
-		return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
+		return results, nil
+
+	case reflect.Struct:
+		valueType := value.Type()
+		for i := 0; i < value.NumField(); i++ {
+			field := valueType.Field(i)
+			sel := selector.Copy()
+			sel.Type = "PROPERTY"
+			sel.Property = field.Name
+			results = append(results, &Node{
+				Value:    reflect.ValueOf(field.Name),
+				Selector: sel,
+			})
+		}
+		return results, nil
+
+	case reflect.Ptr:
+		return findNodesPropertyKeysWork(selector, previousValue, createIfNotExists, derefValue(value))
 	}
 
-	return results, nil
+	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
+}
+
+func findNodesPropertyKeys(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
+	if !isValid(previousValue) {
+		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
+	}
+	if createIfNotExists {
+		return nil, &UnsupportedSelector{Selector: selector.Raw}
+	}
+	return findNodesPropertyKeysWork(selector, previousValue, createIfNotExists, unwrapValue(previousValue))
 }
 
 // findNodesIndex finds the value for the given node using the index selector
@@ -211,14 +248,7 @@ func processFindDynamicItems(selector Selector, object reflect.Value, key string
 	return false, nil
 }
 
-// findNodesDynamic finds the value for the given node using the dynamic selector
-// information.
-func findNodesDynamic(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
-	if !isValid(previousValue) {
-		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
-	}
-	value := unwrapValue(previousValue)
-
+func findNodesDynamicWork(selector Selector, previousValue reflect.Value, createIfNotExists bool, value reflect.Value) ([]*Node, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		results := make([]*Node, 0)
@@ -272,11 +302,47 @@ func findNodesDynamic(selector Selector, previousValue reflect.Value, createIfNo
 			return results, nil
 		}
 		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Struct:
+		results := make([]*Node, 0)
+		valueType := value.Type()
+		for i := 0; i < value.NumField(); i++ {
+			field := valueType.Field(i)
+			object := value.Field(i)
+			found, err := processFindDynamicItems(selector, object, field.Name)
+			if err != nil {
+				return nil, err
+			}
+			if found {
+				selector.Type = "PROPERTY"
+				selector.Property = field.Name
+				results = append(results, &Node{
+					Value:    object,
+					Selector: selector,
+				})
+			}
+		}
+		if len(results) > 0 {
+			return results, nil
+		}
+		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Ptr:
+		return findNodesDynamicWork(selector, previousValue, createIfNotExists, derefValue(value))
 	}
 
 	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
 }
 
+// findNodesDynamic finds the value for the given node using the dynamic selector
+// information.
+func findNodesDynamic(selector Selector, previousValue reflect.Value, createIfNotExists bool) ([]*Node, error) {
+	if !isValid(previousValue) {
+		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
+	}
+	return findNodesDynamicWork(selector, previousValue, createIfNotExists, unwrapValue(previousValue))
+}
+
 func findNodesSearchRecursiveSubNode(selector Selector, subNode *Node, key string, createIfNotExists bool) ([]*Node, error) {
 	subResults, err := findNodesSearchRecursive(selector, subNode, createIfNotExists, false)
 	if err != nil {
@@ -314,14 +380,7 @@ sliceConditionLoop:
 	return results, nil
 }
 
-// findNodesSearchRecursive iterates through the value of the previous node and creates a new node for each element.
-// If any of those nodes match the checks they are returned.
-func findNodesSearchRecursive(selector Selector, previousNode *Node, createIfNotExists bool, firstNode bool) ([]*Node, error) {
-	if !isValid(previousNode.Value) {
-		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
-	}
-	value := unwrapValue(previousNode.Value)
-
+func findNodesSearchRecursiveWork(selector Selector, previousNode *Node, createIfNotExists bool, firstNode bool, value reflect.Value) ([]*Node, error) {
 	results := make([]*Node, 0)
 
 	switch value.Kind() {
@@ -356,17 +415,50 @@ func findNodesSearchRecursive(selector Selector, previousNode *Node, createIfNot
 			subNode.Selector.Type = "PROPERTY"
 			subNode.Selector.Property = fmt.Sprint(key.Interface())
 
-			newResults, err := findNodesSearchRecursiveSubNode(selector, subNode, fmt.Sprint(subNode.Selector.Property), createIfNotExists)
+			newResults, err := findNodesSearchRecursiveSubNode(selector, subNode, subNode.Selector.Property, createIfNotExists)
 			if err != nil {
 				return nil, err
 			}
 			results = append(results, newResults...)
 		}
+
+	case reflect.Struct:
+		valueType := value.Type()
+		for i := 0; i < value.NumField(); i++ {
+			field := valueType.Field(i)
+			object := value.Field(i)
+
+			subNode := &Node{
+				Previous: previousNode,
+				Value:    object,
+				Selector: selector.Copy(),
+			}
+			subNode.Selector.Type = "PROPERTY"
+			subNode.Selector.Property = field.Name
+
+			newResults, err := findNodesSearchRecursiveSubNode(selector, subNode, subNode.Selector.Property, createIfNotExists)
+			if err != nil {
+				return nil, err
+			}
+			results = append(results, newResults...)
+		}
+
+	case reflect.Ptr:
+		return findNodesSearchRecursiveWork(selector, previousNode, createIfNotExists, firstNode, derefValue(value))
 	}
 
 	return results, nil
 }
 
+// findNodesSearchRecursive iterates through the value of the previous node and creates a new node for each element.
+// If any of those nodes match the checks they are returned.
+func findNodesSearchRecursive(selector Selector, previousNode *Node, createIfNotExists bool, firstNode bool) ([]*Node, error) {
+	if !isValid(previousNode.Value) {
+		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
+	}
+	return findNodesSearchRecursiveWork(selector, previousNode, createIfNotExists, firstNode, unwrapValue(previousNode.Value))
+}
+
 // findNodesSearch finds all available nodes by recursively searching the previous value.
 func findNodesSearch(selector Selector, previousNode *Node, createIfNotExists bool) ([]*Node, error) {
 	res, err := findNodesSearchRecursive(selector, previousNode, createIfNotExists, true)
@@ -382,13 +474,7 @@ func findNodesSearch(selector Selector, previousNode *Node, createIfNotExists bo
 	return res, nil
 }
 
-// findNodesAnyIndex returns a node for every value in the previous value list.
-func findNodesAnyIndex(selector Selector, previousValue reflect.Value) ([]*Node, error) {
-	if !isValid(previousValue) {
-		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
-	}
-	value := unwrapValue(previousValue)
-
+func findNodesAnyIndexWork(selector Selector, previousValue reflect.Value, value reflect.Value) ([]*Node, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		results := make([]*Node, 0)
@@ -405,6 +491,7 @@ func findNodesAnyIndex(selector Selector, previousValue reflect.Value) ([]*Node,
 			return results, nil
 		}
 		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
 	case reflect.Map:
 		results := make([]*Node, 0)
 		for _, key := range value.MapKeys() {
@@ -420,19 +507,41 @@ func findNodesAnyIndex(selector Selector, previousValue reflect.Value) ([]*Node,
 			return results, nil
 		}
 		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Struct:
+		valueType := value.Type()
+		results := make([]*Node, 0)
+		for i := 0; i < valueType.NumField(); i++ {
+			field := valueType.Field(i)
+			object := value.Field(i)
+			selector.Type = "PROPERTY"
+			selector.Property = field.Name
+			results = append(results, &Node{
+				Value:    object,
+				Selector: selector,
+			})
+		}
+		if len(results) > 0 {
+			return results, nil
+		}
+		return nil, &ValueNotFound{Selector: selector.Current, PreviousValue: previousValue}
+
+	case reflect.Ptr:
+		return findNodesAnyIndexWork(selector, previousValue, derefValue(previousValue))
 	}
 
 	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
 }
 
-// findNodesLength returns the length
-func findNodesLength(selector Selector, previousValue reflect.Value) ([]*Node, error) {
+// findNodesAnyIndex returns a node for every value in the previous value list.
+func findNodesAnyIndex(selector Selector, previousValue reflect.Value) ([]*Node, error) {
 	if !isValid(previousValue) {
 		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
 	}
+	return findNodesAnyIndexWork(selector, previousValue, unwrapValue(previousValue))
+}
 
-	value := unwrapValue(previousValue)
-
+func findNodesLengthWork(selector Selector, previousValue reflect.Value, value reflect.Value) ([]*Node, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		node := &Node{
@@ -454,19 +563,30 @@ func findNodesLength(selector Selector, previousValue reflect.Value) ([]*Node, e
 			Selector: selector,
 		}
 		return []*Node{node}, nil
+
+	case reflect.Struct:
+		node := &Node{
+			Value:    reflect.ValueOf(value.NumField()),
+			Selector: selector,
+		}
+		return []*Node{node}, nil
+
+	case reflect.Ptr:
+		return findNodesLengthWork(selector, previousValue, derefValue(value))
 	}
 
 	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
 }
 
-// findNodesType returns the length
-func findNodesType(selector Selector, previousValue reflect.Value) ([]*Node, error) {
+// findNodesLength returns the length
+func findNodesLength(selector Selector, previousValue reflect.Value) ([]*Node, error) {
 	if !isValid(previousValue) {
 		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
 	}
+	return findNodesLengthWork(selector, previousValue, unwrapValue(previousValue))
+}
 
-	value := unwrapValue(previousValue)
-
+func findNodesTypeWork(selector Selector, value reflect.Value) ([]*Node, error) {
 	switch value.Kind() {
 	case reflect.Slice:
 		node := &Node{
@@ -509,11 +629,29 @@ func findNodesType(selector Selector, previousValue reflect.Value) ([]*Node, err
 			Selector: selector,
 		}
 		return []*Node{node}, nil
+
+	case reflect.Struct:
+		node := &Node{
+			Value:    reflect.ValueOf("struct"),
+			Selector: selector,
+		}
+		return []*Node{node}, nil
+
+	case reflect.Ptr:
+		return findNodesTypeWork(selector, derefValue(value))
 	}
 
 	return nil, &UnsupportedTypeForSelector{Selector: selector, Value: value}
 }
 
+// findNodesType returns the type
+func findNodesType(selector Selector, previousValue reflect.Value) ([]*Node, error) {
+	if !isValid(previousValue) {
+		return nil, &UnexpectedPreviousNilValue{Selector: selector.Raw}
+	}
+	return findNodesTypeWork(selector, unwrapValue(previousValue))
+}
+
 func initialiseEmptyValue(selector Selector, previousValue reflect.Value) reflect.Value {
 	switch selector.Type {
 	case "PROPERTY":


Test patch:
diff --git a/node_test.go b/node_test.go
index ab1b831..010d82d 100644
--- a/node_test.go
+++ b/node_test.go
@@ -674,6 +674,50 @@ func TestNode_PutMultiple(t *testing.T) {
 			},
 		}
 
+		if !reflect.DeepEqual(exp, value) {
+			t.Errorf("expected %v, got %v", exp, value)
+		}
+	})
+	t.Run("StructAnyIndex", func(t *testing.T) {
+		type user struct {
+			Name string
+			Age  int
+		}
+		value := []user{
+			{
+				Name: "Tom",
+				Age:  27,
+			},
+			{
+				Name: "Jim",
+				Age:  27,
+			},
+			{
+				Name: "Amelia",
+				Age:  25,
+			},
+		}
+		err := dasel.New(value).PutMultiple(".[*].Name", "Frank")
+		if err != nil {
+			t.Errorf("unexpected query error: %s", err)
+			return
+		}
+
+		exp := []user{
+			{
+				Name: "Frank",
+				Age:  27,
+			},
+			{
+				Name: "Frank",
+				Age:  27,
+			},
+			{
+				Name: "Frank",
+				Age:  25,
+			},
+		}
+
 		if !reflect.DeepEqual(exp, value) {
 			t.Errorf("expected %v, got %v", exp, value)
 		}
@@ -855,6 +899,42 @@ func TestNode_Query_Data(t *testing.T) {
 			return
 		}
 
+		if exp, got := "Tom", got.InterfaceValue().(string); exp != got {
+			t.Errorf("expected %s, got %s", exp, got)
+		}
+	})
+	t.Run("SelectFromStruct", func(t *testing.T) {
+		rootNode := dasel.New([]struct {
+			Name string
+		}{
+			{Name: "Tom"},
+			{Name: "Jim"},
+		})
+
+		got, err := rootNode.Query(".[0].Name")
+		if err != nil {
+			t.Errorf("unexpected query error: %v", err)
+			return
+		}
+
+		if exp, got := "Tom", got.InterfaceValue().(string); exp != got {
+			t.Errorf("expected %s, got %s", exp, got)
+		}
+	})
+	t.Run("ConditionalSelectFromStruct", func(t *testing.T) {
+		rootNode := dasel.New([]struct {
+			Name string
+		}{
+			{Name: "Tom"},
+			{Name: "Jim"},
+		})
+
+		got, err := rootNode.Query(".(Name=Tom).Name")
+		if err != nil {
+			t.Errorf("unexpected query error: %v", err)
+			return
+		}
+
 		if exp, got := "Tom", got.InterfaceValue().(string); exp != got {
 			t.Errorf("expected %s, got %s", exp, got)
 		}
@@ -1138,6 +1218,28 @@ func TestNode_Delete(t *testing.T) {
 			},
 		},
 	}))
+	t.Run("ExistingStringInStruct", deleteTest(dasel.New(&struct {
+		Name string
+		Age  int
+	}{
+		Name: "Tom",
+		Age:  123,
+	}), ".Name", &struct {
+		Name string
+		Age  int
+	}{
+		Age: 123,
+	}))
+	t.Run("AnyIndexInStruct", deleteMultipleTest(dasel.New(&struct {
+		Name string
+		Age  int
+	}{
+		Name: "Tom",
+		Age:  123,
+	}), ".[*]", &struct {
+		Name string
+		Age  int
+	}{}))
 	t.Run("ExistingObjectInArray", deleteTest(dasel.New(data()), "people.[0]", map[string]interface{}{
 		"id": "123",
 		"names": []string{