1
0
mirror of https://github.com/TomWright/dasel.git synced 2022-05-22 02:32:45 +03:00
Files
dasel-data-selector/node_propagate.go
2021-08-01 22:48:13 +01:00

209 lines
5.4 KiB
Go

package dasel
import (
"fmt"
"reflect"
)
// propagate recursively propagates the given nodes value up to the root node.
func propagate(n *Node) error {
if n.Previous == nil {
return nil
}
if err := propagateValue(n); err != nil {
return fmt.Errorf("could not propagate value: %w", err)
}
return propagate(n.Previous)
}
// propagateValue sends the value of the current node up to the previous node in the chain.
func propagateValue(n *Node) error {
if n.Previous == nil {
return nil
}
switch n.Selector.Type {
case "PROPERTY":
return propagateValueProperty(n)
case "INDEX":
return propagateValueIndex(n)
case "NEXT_AVAILABLE_INDEX":
return propagateValueNextAvailableIndex(n)
default:
return &UnsupportedSelector{Selector: n.Selector.Type}
}
}
// 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 {
value.SetMapIndex(reflect.ValueOf(n.Selector.Property), n.Value)
return nil
}
return &UnsupportedTypeForSelector{Selector: n.Selector, Value: n.Previous.Value}
}
// propagateValueIndex sends the value of the current node up to the previous node in the chain.
func propagateValueIndex(n *Node) error {
if !isValid(n.Previous.Value) {
return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
}
value := unwrapValue(n.Previous.Value)
if value.Kind() == reflect.Slice {
if n.Selector.Index >= 0 && n.Selector.Index < value.Len() {
value.Index(n.Selector.Index).Set(n.Value)
return nil
}
n.Previous.setReflectValue(reflect.Append(value, n.Value))
return nil
}
return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// propagateValueNextAvailableIndex sends the value of the current node up to the previous node in the chain.
func propagateValueNextAvailableIndex(n *Node) error {
if !isValid(n.Previous.Value) {
return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
}
value := unwrapValue(n.Previous.Value)
if value.Kind() == reflect.Slice {
n.Previous.setReflectValue(reflect.Append(value, n.Value))
return nil
}
return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// deleteFromParent deletes the given node from it's parent.
func deleteFromParent(n *Node) error {
if n.Previous == nil {
return nil
}
switch n.Selector.Type {
case "PROPERTY":
return deleteFromParentProperty(n)
case "INDEX":
return deleteFromParentIndex(n)
default:
return &UnsupportedSelector{Selector: n.Selector.Type}
}
}
// 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 {
value.SetMapIndex(reflect.ValueOf(n.Selector.Property), reflect.Value{})
return nil
}
return &UnsupportedTypeForSelector{Selector: n.Selector, Value: 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) {
return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
}
value := unwrapValue(n.Previous.Value)
if value.Kind() == reflect.Slice {
if n.Selector.Index >= 0 && n.Selector.Index < value.Len() {
// Mark this index for deletion.
// We can't just rewrite the slice here in-case other selectors also target it.
value.Index(n.Selector.Index).Set(getDeletePlaceholder(value.Index(n.Selector.Index)))
}
return nil
}
return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
}
// cleanupSliceDeletions scans through the given reflect and removes any invalid reflect values.
// Returns false if no modification was made.
func cleanupSliceDeletions(input reflect.Value) (reflect.Value, bool) {
value := unwrapValue(input)
if value.Kind() != reflect.Slice {
return value, false
}
res := reflect.MakeSlice(value.Type(), 0, value.Len())
invalidCount := 0
for i := 0; i < value.Len(); i++ {
item := value.Index(i)
if !item.IsValid() || isDeletePlaceholder(item) {
invalidCount++
continue
}
res = reflect.Append(res, item)
}
if invalidCount == 0 {
return value, false
}
return res, true
}
const deletePlaceholderKey = "dasel:delete:key"
const deletePlaceholder = "dasel:delete:me"
func getDeletePlaceholder(item reflect.Value) reflect.Value {
switch unwrapValue(item).Kind() {
case reflect.Map:
return reflect.ValueOf(map[string]interface{}{
deletePlaceholderKey: deletePlaceholder,
})
case reflect.Slice:
return reflect.ValueOf([]interface{}{deletePlaceholder})
default:
return reflect.ValueOf(deletePlaceholder)
}
}
func isDeletePlaceholder(item reflect.Value) bool {
switch i := unwrapValue(item); i.Kind() {
case reflect.Map:
if val, ok := i.Interface().(map[string]interface{})[deletePlaceholderKey]; ok {
if val == deletePlaceholder {
return true
}
}
case reflect.Slice:
for _, val := range i.Interface().([]interface{}) {
if val == deletePlaceholder {
return true
}
}
default:
if val, ok := i.Interface().(string); ok {
if val == deletePlaceholder {
return true
}
}
}
return false
}