consul/vendor/github.com/mitchellh/pointerstructure/pointer.go

124 lines
3.5 KiB
Go
Raw Normal View History

2020-05-12 01:59:29 +00:00
// Package pointerstructure provides functions for identifying a specific
// value within any Go structure using a string syntax.
//
// The syntax used is based on JSON Pointer (RFC 6901).
package pointerstructure
import (
"fmt"
"reflect"
"strings"
"github.com/mitchellh/mapstructure"
)
// Pointer represents a pointer to a specific value. You can construct
// a pointer manually or use Parse.
type Pointer struct {
// Parts are the pointer parts. No escape codes are processed here.
// The values are expected to be exact. If you have escape codes, use
// the Parse functions.
Parts []string
}
// Get reads the value at the given pointer.
//
// This is a shorthand for calling Parse on the pointer and then calling Get
// on that result. An error will be returned if the value cannot be found or
// there is an error with the format of pointer.
func Get(value interface{}, pointer string) (interface{}, error) {
p, err := Parse(pointer)
if err != nil {
return nil, err
}
return p.Get(value)
}
// Set sets the value at the given pointer.
//
// This is a shorthand for calling Parse on the pointer and then calling Set
// on that result. An error will be returned if the value cannot be found or
// there is an error with the format of pointer.
//
// Set returns the complete document, which might change if the pointer value
// points to the root ("").
func Set(doc interface{}, pointer string, value interface{}) (interface{}, error) {
p, err := Parse(pointer)
if err != nil {
return nil, err
}
return p.Set(doc, value)
}
// String returns the string value that can be sent back to Parse to get
// the same Pointer result.
func (p *Pointer) String() string {
if len(p.Parts) == 0 {
return ""
}
// Copy the parts so we can convert back the escapes
result := make([]string, len(p.Parts))
copy(result, p.Parts)
for i, p := range p.Parts {
result[i] = strings.Replace(
strings.Replace(p, "~", "~0", -1), "/", "~1", -1)
}
return "/" + strings.Join(result, "/")
}
// Parent returns a pointer to the parent element of this pointer.
//
// If Pointer represents the root (empty parts), a pointer representing
// the root is returned. Therefore, to check for the root, IsRoot() should be
// called.
func (p *Pointer) Parent() *Pointer {
// If this is root, then we just return a new root pointer. We allocate
// a new one though so this can still be modified.
if p.IsRoot() {
return &Pointer{}
}
parts := make([]string, len(p.Parts)-1)
copy(parts, p.Parts[:len(p.Parts)-1])
return &Pointer{
Parts: parts,
}
}
// IsRoot returns true if this pointer represents the root document.
func (p *Pointer) IsRoot() bool {
return len(p.Parts) == 0
}
// coerce is a helper to coerce a value to a specific type if it must
// and if its possible. If it isn't possible, an error is returned.
func coerce(value reflect.Value, to reflect.Type) (reflect.Value, error) {
// If the value is already assignable to the type, then let it go
if value.Type().AssignableTo(to) {
return value, nil
}
// If a direct conversion is possible, do that
if value.Type().ConvertibleTo(to) {
return value.Convert(to), nil
}
// Create a new value to hold our result
result := reflect.New(to)
// Decode
if err := mapstructure.WeakDecode(value.Interface(), result.Interface()); err != nil {
return result, fmt.Errorf(
"couldn't convert value %#v to type %s",
value.Interface(), to.String())
}
// We need to indirect the value since reflect.New always creates a pointer
return reflect.Indirect(result), nil
}