github.com/tomwright/dasel@v1.27.3/node_propagate.go (about)

     1  package dasel
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  // propagate recursively propagates the given nodes value up to the root node.
     9  func propagate(n *Node) error {
    10  	if n.Previous == nil {
    11  		return nil
    12  	}
    13  
    14  	if err := propagateValue(n); err != nil {
    15  		return fmt.Errorf("could not propagate value: %w", err)
    16  	}
    17  	return propagate(n.Previous)
    18  }
    19  
    20  // propagateValue sends the value of the current node up to the previous node in the chain.
    21  func propagateValue(n *Node) error {
    22  	if n.Previous == nil {
    23  		return nil
    24  	}
    25  
    26  	switch n.Selector.Type {
    27  	case "PROPERTY":
    28  		return propagateValueProperty(n)
    29  	case "INDEX":
    30  		return propagateValueIndex(n)
    31  	case "NEXT_AVAILABLE_INDEX":
    32  		return propagateValueNextAvailableIndex(n)
    33  	default:
    34  		return &UnsupportedSelector{Selector: n.Selector.Type}
    35  	}
    36  }
    37  
    38  func propagateValuePropertyWork(n *Node, value reflect.Value) error {
    39  	switch value.Kind() {
    40  	case reflect.Map:
    41  		value.SetMapIndex(reflect.ValueOf(n.Selector.Property), n.Value)
    42  		return nil
    43  	case reflect.Struct:
    44  		fieldV := value.FieldByName(n.Selector.Property)
    45  		if fieldV.IsValid() {
    46  			fieldV.Set(n.Value)
    47  		}
    48  		return nil
    49  	case reflect.Ptr:
    50  		return propagateValuePropertyWork(n, derefValue(value))
    51  	}
    52  
    53  	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: n.Previous.Value}
    54  }
    55  
    56  // propagateValueProperty sends the value of the current node up to the previous node in the chain.
    57  func propagateValueProperty(n *Node) error {
    58  	if !isValid(n.Previous.Value) {
    59  		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
    60  	}
    61  	return propagateValuePropertyWork(n, unwrapValue(n.Previous.Value))
    62  }
    63  
    64  // propagateValueIndex sends the value of the current node up to the previous node in the chain.
    65  // No need to support structs here since a struct can't have an index.
    66  func propagateValueIndex(n *Node) error {
    67  	if !isValid(n.Previous.Value) {
    68  		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
    69  	}
    70  
    71  	value := unwrapValue(n.Previous.Value)
    72  
    73  	if value.Kind() == reflect.Slice {
    74  		if n.Selector.Index >= 0 && n.Selector.Index < value.Len() {
    75  			value.Index(n.Selector.Index).Set(n.Value)
    76  			return nil
    77  		}
    78  		n.Previous.setReflectValue(reflect.Append(value, n.Value))
    79  		return nil
    80  	}
    81  
    82  	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
    83  }
    84  
    85  // propagateValueNextAvailableIndex sends the value of the current node up to the previous node in the chain.
    86  func propagateValueNextAvailableIndex(n *Node) error {
    87  	if !isValid(n.Previous.Value) {
    88  		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
    89  	}
    90  
    91  	value := unwrapValue(n.Previous.Value)
    92  
    93  	if value.Kind() == reflect.Slice {
    94  		n.Previous.setReflectValue(reflect.Append(value, n.Value))
    95  		return nil
    96  	}
    97  
    98  	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
    99  }
   100  
   101  // deleteFromParent deletes the given node from it's parent.
   102  func deleteFromParent(n *Node) error {
   103  	if n.Previous == nil {
   104  		return nil
   105  	}
   106  
   107  	switch n.Selector.Type {
   108  	case "PROPERTY":
   109  		return deleteFromParentProperty(n)
   110  	case "INDEX":
   111  		return deleteFromParentIndex(n)
   112  	default:
   113  		return &UnsupportedSelector{Selector: n.Selector.Type}
   114  	}
   115  }
   116  
   117  func deleteFromParentPropertyWork(n *Node, value reflect.Value) error {
   118  	switch value.Kind() {
   119  	case reflect.Map:
   120  		value.SetMapIndex(reflect.ValueOf(n.Selector.Property), reflect.Value{})
   121  		return nil
   122  	case reflect.Struct:
   123  		fieldV := value.FieldByName(n.Selector.Property)
   124  		if fieldV.CanSet() && fieldV.IsValid() && !fieldV.IsZero() {
   125  			fieldV.Set(reflect.New(fieldV.Type()).Elem())
   126  		}
   127  		return nil
   128  	case reflect.Ptr:
   129  		return deleteFromParentPropertyWork(n, derefValue(value))
   130  	}
   131  
   132  	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: n.Previous.Value}
   133  }
   134  
   135  // deleteFromParentProperty sends the value of the current node up to the previous node in the chain.
   136  func deleteFromParentProperty(n *Node) error {
   137  	if !isValid(n.Previous.Value) {
   138  		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
   139  	}
   140  	return deleteFromParentPropertyWork(n, unwrapValue(n.Previous.Value))
   141  }
   142  
   143  // deleteFromParentIndex sends the value of the current node up to the previous node in the chain.
   144  func deleteFromParentIndex(n *Node) error {
   145  	if !isValid(n.Previous.Value) {
   146  		return &UnexpectedPreviousNilValue{Selector: n.Previous.Selector.Current}
   147  	}
   148  
   149  	value := unwrapValue(n.Previous.Value)
   150  
   151  	if value.Kind() == reflect.Slice {
   152  		if n.Selector.Index >= 0 && n.Selector.Index < value.Len() {
   153  			// Mark this index for deletion.
   154  			// We can't just rewrite the slice here in-case other selectors also target it.
   155  			value.Index(n.Selector.Index).Set(getDeletePlaceholder(value.Index(n.Selector.Index)))
   156  		}
   157  		return nil
   158  	}
   159  
   160  	return &UnsupportedTypeForSelector{Selector: n.Selector, Value: value}
   161  }
   162  
   163  // cleanupSliceDeletions scans through the given reflect and removes any invalid reflect values.
   164  // Returns false if no modification was made.
   165  func cleanupSliceDeletions(input reflect.Value) (reflect.Value, bool) {
   166  	value := unwrapValue(input)
   167  	if value.Kind() != reflect.Slice {
   168  		return value, false
   169  	}
   170  	res := reflect.MakeSlice(value.Type(), 0, value.Len())
   171  
   172  	invalidCount := 0
   173  
   174  	for i := 0; i < value.Len(); i++ {
   175  		item := value.Index(i)
   176  		if !item.IsValid() || isDeletePlaceholder(item) {
   177  			invalidCount++
   178  			continue
   179  		}
   180  		res = reflect.Append(res, item)
   181  	}
   182  
   183  	if invalidCount == 0 {
   184  		return value, false
   185  	}
   186  
   187  	return res, true
   188  }
   189  
   190  const deletePlaceholderKey = "dasel:delete:key"
   191  const deletePlaceholder = "dasel:delete:me"
   192  
   193  func getDeletePlaceholder(item reflect.Value) reflect.Value {
   194  	switch unwrapValue(item).Kind() {
   195  	case reflect.Map:
   196  		return reflect.ValueOf(map[string]interface{}{
   197  			deletePlaceholderKey: deletePlaceholder,
   198  		})
   199  	case reflect.Slice:
   200  		return reflect.ValueOf([]interface{}{deletePlaceholder})
   201  	default:
   202  		return reflect.ValueOf(deletePlaceholder)
   203  	}
   204  }
   205  
   206  func isDeletePlaceholder(item reflect.Value) bool {
   207  	// No need to handle structs since on delete we set the field to a zero value.
   208  	switch i := unwrapValue(item); i.Kind() {
   209  	case reflect.Map:
   210  		if val, ok := i.Interface().(map[string]interface{})[deletePlaceholderKey]; ok {
   211  			if val == deletePlaceholder {
   212  				return true
   213  			}
   214  		}
   215  	case reflect.Slice:
   216  		for _, val := range i.Interface().([]interface{}) {
   217  			if val == deletePlaceholder {
   218  				return true
   219  			}
   220  		}
   221  	default:
   222  		if val, ok := i.Interface().(string); ok {
   223  			if val == deletePlaceholder {
   224  				return true
   225  			}
   226  		}
   227  	}
   228  
   229  	return false
   230  }