This repository was archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathpointer.go
More file actions
145 lines (123 loc) · 4.38 KB
/
pointer.go
File metadata and controls
145 lines (123 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// 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"
)
// ValueTransformationHookFn transforms a Go data structure into another.
// This is useful for situations where you want the JSON Pointer to not be an
// exact match to the structure of the Go struct or map, for example when
// working with protocol buffers' well-known types.
type ValueTransformationHookFn func(reflect.Value) reflect.Value
type Config struct {
// The tag name that pointerstructure reads for field names. This
// defaults to "pointer"
TagName string
// ValueTransformationHook is called on each reference token within the
// provided JSON Pointer when Get is used. The returned value from this
// hook is then used for matching for all following parts of the JSON
// Pointer. If this returns a nil interface Get will return an error.
ValueTransformationHook ValueTransformationHookFn
}
// 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
// Config is the configuration controlling how items are looked up
// in structures.
Config Config
}
// 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,
Config: p.Config,
}
}
// 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(
"%w %#v to type %s", ErrConvert,
value.Interface(), to.String())
}
// We need to indirect the value since reflect.New always creates a pointer
return reflect.Indirect(result), nil
}