Skip to content

Commit 779f453

Browse files
committed
adds a set method
1 parent 8d96a2d commit 779f453

File tree

3 files changed

+463
-25
lines changed

3 files changed

+463
-25
lines changed

.editorconfig

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# top-most EditorConfig file
2+
root = true
3+
4+
# Unix-style newlines with a newline ending every file
5+
[*]
6+
end_of_line = lf
7+
insert_final_newline = true
8+
indent_style = space
9+
indent_size = 2
10+
trim_trailing_whitespace = true
11+
12+
# Set default charset
13+
[*.{js,py,go,scala,rb,java,html,css,less,sass,md}]
14+
charset = utf-8
15+
16+
# Tab indentation (no size specified)
17+
[*.go]
18+
indent_style = tab
19+
20+
[*.md]
21+
trim_trailing_whitespace = false
22+
23+
# Matches the exact files either package.json or .travis.yml
24+
[{package.json,.travis.yml}]
25+
indent_style = space
26+
indent_size = 2

pointer.go

+165-13
Original file line numberDiff line numberDiff line change
@@ -43,23 +43,18 @@ const (
4343
)
4444

4545
var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
46+
var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
4647

4748
// JSONPointable is an interface for structs to implement when they need to customize the
4849
// json pointer process
4950
type JSONPointable interface {
5051
JSONLookup(string) (interface{}, error)
5152
}
5253

53-
type implStruct struct {
54-
mode string // "SET" or "GET"
55-
56-
inDocument interface{}
57-
58-
setInValue interface{}
59-
60-
getOutNode interface{}
61-
getOutKind reflect.Kind
62-
outError error
54+
// JSONSetable is an interface for structs to implement when they need to customize the
55+
// json pointer process
56+
type JSONSetable interface {
57+
JSONSet(string, interface{}) error
6358
}
6459

6560
// New creates a new json pointer for the given string
@@ -100,15 +95,25 @@ func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
10095
return p.get(document, swag.DefaultJSONNameProvider)
10196
}
10297

98+
// Set uses the pointer to set a value from a JSON document
99+
func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
100+
return document, p.set(document, value, swag.DefaultJSONNameProvider)
101+
}
102+
103103
// GetForToken gets a value for a json pointer token 1 level deep
104104
func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
105105
return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
106106
}
107107

108+
// SetForToken gets a value for a json pointer token 1 level deep
109+
func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
110+
return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
111+
}
112+
108113
func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
109-
kind := reflect.Invalid
110114
rValue := reflect.Indirect(reflect.ValueOf(node))
111-
kind = rValue.Kind()
115+
kind := rValue.Kind()
116+
112117
switch kind {
113118

114119
case reflect.Struct:
@@ -129,6 +134,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
129134
case reflect.Map:
130135
kv := reflect.ValueOf(decodedToken)
131136
mv := rValue.MapIndex(kv)
137+
132138
if mv.IsValid() && !swag.IsZero(mv) {
133139
return mv.Interface(), kind, nil
134140
}
@@ -141,7 +147,7 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
141147
}
142148
sLength := rValue.Len()
143149
if tokenIndex < 0 || tokenIndex >= sLength {
144-
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
150+
return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
145151
}
146152

147153
elem := rValue.Index(tokenIndex)
@@ -153,6 +159,57 @@ func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.Nam
153159

154160
}
155161

162+
func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
163+
rValue := reflect.Indirect(reflect.ValueOf(node))
164+
switch rValue.Kind() {
165+
166+
case reflect.Struct:
167+
if ns, ok := node.(JSONSetable); ok { // pointer impl
168+
return ns.JSONSet(decodedToken, data)
169+
}
170+
171+
if rValue.Type().Implements(jsonSetableType) {
172+
return node.(JSONSetable).JSONSet(decodedToken, data)
173+
}
174+
175+
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
176+
if !ok {
177+
return fmt.Errorf("object has no field %q", decodedToken)
178+
}
179+
fld := rValue.FieldByName(nm)
180+
if fld.IsValid() {
181+
fld.Set(reflect.ValueOf(data))
182+
}
183+
return nil
184+
185+
case reflect.Map:
186+
kv := reflect.ValueOf(decodedToken)
187+
rValue.SetMapIndex(kv, reflect.ValueOf(data))
188+
return nil
189+
190+
case reflect.Slice:
191+
tokenIndex, err := strconv.Atoi(decodedToken)
192+
if err != nil {
193+
return err
194+
}
195+
sLength := rValue.Len()
196+
if tokenIndex < 0 || tokenIndex >= sLength {
197+
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
198+
}
199+
200+
elem := rValue.Index(tokenIndex)
201+
if !elem.CanSet() {
202+
return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
203+
}
204+
elem.Set(reflect.ValueOf(data))
205+
return nil
206+
207+
default:
208+
return fmt.Errorf("invalid token reference %q", decodedToken)
209+
}
210+
211+
}
212+
156213
func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
157214

158215
if nameProvider == nil {
@@ -184,6 +241,101 @@ func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interf
184241
return node, kind, nil
185242
}
186243

244+
func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
245+
knd := reflect.ValueOf(node).Kind()
246+
247+
if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
248+
return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
249+
}
250+
251+
if nameProvider == nil {
252+
nameProvider = swag.DefaultJSONNameProvider
253+
}
254+
255+
// Full document when empty
256+
if len(p.referenceTokens) == 0 {
257+
return nil
258+
}
259+
260+
lastI := len(p.referenceTokens) - 1
261+
for i, token := range p.referenceTokens {
262+
isLastToken := i == lastI
263+
decodedToken := Unescape(token)
264+
265+
if isLastToken {
266+
267+
return setSingleImpl(node, data, decodedToken, nameProvider)
268+
}
269+
270+
rValue := reflect.Indirect(reflect.ValueOf(node))
271+
kind := rValue.Kind()
272+
273+
switch kind {
274+
275+
case reflect.Struct:
276+
if rValue.Type().Implements(jsonPointableType) {
277+
r, err := node.(JSONPointable).JSONLookup(decodedToken)
278+
if err != nil {
279+
return err
280+
}
281+
fld := reflect.ValueOf(r)
282+
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
283+
node = fld.Addr().Interface()
284+
continue
285+
}
286+
node = r
287+
continue
288+
}
289+
nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
290+
if !ok {
291+
return fmt.Errorf("object has no field %q", decodedToken)
292+
}
293+
fld := rValue.FieldByName(nm)
294+
if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
295+
node = fld.Addr().Interface()
296+
continue
297+
}
298+
node = fld.Interface()
299+
300+
case reflect.Map:
301+
kv := reflect.ValueOf(decodedToken)
302+
mv := rValue.MapIndex(kv)
303+
304+
if !mv.IsValid() {
305+
return fmt.Errorf("object has no key %q", decodedToken)
306+
}
307+
if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
308+
node = mv.Addr().Interface()
309+
continue
310+
}
311+
node = mv.Interface()
312+
313+
case reflect.Slice:
314+
tokenIndex, err := strconv.Atoi(decodedToken)
315+
if err != nil {
316+
return err
317+
}
318+
sLength := rValue.Len()
319+
if tokenIndex < 0 || tokenIndex >= sLength {
320+
return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
321+
}
322+
323+
elem := rValue.Index(tokenIndex)
324+
if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
325+
node = elem.Addr().Interface()
326+
continue
327+
}
328+
node = elem.Interface()
329+
330+
default:
331+
return fmt.Errorf("invalid token reference %q", decodedToken)
332+
}
333+
334+
}
335+
336+
return nil
337+
}
338+
187339
// DecodedTokens returns the decoded tokens
188340
func (p *Pointer) DecodedTokens() []string {
189341
result := make([]string, 0, len(p.referenceTokens))

0 commit comments

Comments
 (0)