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 }