mirror of https://github.com/hashicorp/consul
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
|
package pointerstructure
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// Delete deletes the value specified by the pointer p in structure s.
|
||
|
//
|
||
|
// When deleting a slice index, all other elements will be shifted to
|
||
|
// the left. This is specified in RFC6902 (JSON Patch) and not RFC6901 since
|
||
|
// RFC6901 doesn't specify operations on pointers. If you don't want to
|
||
|
// shift elements, you should use Set to set the slice index to the zero value.
|
||
|
//
|
||
|
// The structures s must have non-zero values set up to this pointer.
|
||
|
// For example, if deleting "/bob/0/name", then "/bob/0" must be set already.
|
||
|
//
|
||
|
// The returned value is potentially a new value if this pointer represents
|
||
|
// the root document. Otherwise, the returned value will always be s.
|
||
|
func (p *Pointer) Delete(s interface{}) (interface{}, error) {
|
||
|
// if we represent the root doc, we've deleted everything
|
||
|
if len(p.Parts) == 0 {
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
// Save the original since this is going to be our return value
|
||
|
originalS := s
|
||
|
|
||
|
// Get the parent value
|
||
|
var err error
|
||
|
s, err = p.Parent().Get(s)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
// Map for lookup of getter to call for type
|
||
|
funcMap := map[reflect.Kind]deleteFunc{
|
||
|
reflect.Array: p.deleteSlice,
|
||
|
reflect.Map: p.deleteMap,
|
||
|
reflect.Slice: p.deleteSlice,
|
||
|
}
|
||
|
|
||
|
val := reflect.ValueOf(s)
|
||
|
for val.Kind() == reflect.Interface {
|
||
|
val = val.Elem()
|
||
|
}
|
||
|
|
||
|
for val.Kind() == reflect.Ptr {
|
||
|
val = reflect.Indirect(val)
|
||
|
}
|
||
|
|
||
|
f, ok := funcMap[val.Kind()]
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("delete %s: invalid value kind: %s", p, val.Kind())
|
||
|
}
|
||
|
|
||
|
result, err := f(originalS, val)
|
||
|
if err != nil {
|
||
|
return nil, fmt.Errorf("delete %s: %s", p, err)
|
||
|
}
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
type deleteFunc func(interface{}, reflect.Value) (interface{}, error)
|
||
|
|
||
|
func (p *Pointer) deleteMap(root interface{}, m reflect.Value) (interface{}, error) {
|
||
|
part := p.Parts[len(p.Parts)-1]
|
||
|
key, err := coerce(reflect.ValueOf(part), m.Type().Key())
|
||
|
if err != nil {
|
||
|
return root, err
|
||
|
}
|
||
|
|
||
|
// Delete the key
|
||
|
var elem reflect.Value
|
||
|
m.SetMapIndex(key, elem)
|
||
|
return root, nil
|
||
|
}
|
||
|
|
||
|
func (p *Pointer) deleteSlice(root interface{}, s reflect.Value) (interface{}, error) {
|
||
|
// Coerce the key to an int
|
||
|
part := p.Parts[len(p.Parts)-1]
|
||
|
idxVal, err := coerce(reflect.ValueOf(part), reflect.TypeOf(42))
|
||
|
if err != nil {
|
||
|
return root, err
|
||
|
}
|
||
|
idx := int(idxVal.Int())
|
||
|
|
||
|
// Verify we're within bounds
|
||
|
if idx < 0 || idx >= s.Len() {
|
||
|
return root, fmt.Errorf(
|
||
|
"index %d is out of range (length = %d)", idx, s.Len())
|
||
|
}
|
||
|
|
||
|
// Mimicing the following with reflection to do this:
|
||
|
//
|
||
|
// copy(a[i:], a[i+1:])
|
||
|
// a[len(a)-1] = nil // or the zero value of T
|
||
|
// a = a[:len(a)-1]
|
||
|
|
||
|
// copy(a[i:], a[i+1:])
|
||
|
reflect.Copy(s.Slice(idx, s.Len()), s.Slice(idx+1, s.Len()))
|
||
|
|
||
|
// a[len(a)-1] = nil // or the zero value of T
|
||
|
s.Index(s.Len() - 1).Set(reflect.Zero(s.Type().Elem()))
|
||
|
|
||
|
// a = a[:len(a)-1]
|
||
|
s = s.Slice(0, s.Len()-1)
|
||
|
|
||
|
// set the slice back on the parent
|
||
|
return p.Parent().Set(root, s.Interface())
|
||
|
}
|