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