podman
390 строк · 10.4 Кб
1// Copyright 2013 sigu-399 ( https://github.com/sigu-399 )
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// author sigu-399
16// author-github https://github.com/sigu-399
17// author-mail sigu.399@gmail.com
18//
19// repository-name jsonpointer
20// repository-desc An implementation of JSON Pointer - Go language
21//
22// description Main and unique file.
23//
24// created 25-02-2013
25
26package jsonpointer
27
28import (
29"errors"
30"fmt"
31"reflect"
32"strconv"
33"strings"
34
35"github.com/go-openapi/swag"
36)
37
38const (
39emptyPointer = ``
40pointerSeparator = `/`
41
42invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator
43)
44
45var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem()
46var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem()
47
48// JSONPointable is an interface for structs to implement when they need to customize the
49// json pointer process
50type JSONPointable interface {
51JSONLookup(string) (interface{}, error)
52}
53
54// JSONSetable is an interface for structs to implement when they need to customize the
55// json pointer process
56type JSONSetable interface {
57JSONSet(string, interface{}) error
58}
59
60// New creates a new json pointer for the given string
61func New(jsonPointerString string) (Pointer, error) {
62
63var p Pointer
64err := p.parse(jsonPointerString)
65return p, err
66
67}
68
69// Pointer the json pointer reprsentation
70type Pointer struct {
71referenceTokens []string
72}
73
74// "Constructor", parses the given string JSON pointer
75func (p *Pointer) parse(jsonPointerString string) error {
76
77var err error
78
79if jsonPointerString != emptyPointer {
80if !strings.HasPrefix(jsonPointerString, pointerSeparator) {
81err = errors.New(invalidStart)
82} else {
83referenceTokens := strings.Split(jsonPointerString, pointerSeparator)
84for _, referenceToken := range referenceTokens[1:] {
85p.referenceTokens = append(p.referenceTokens, referenceToken)
86}
87}
88}
89
90return err
91}
92
93// Get uses the pointer to retrieve a value from a JSON document
94func (p *Pointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
95return p.get(document, swag.DefaultJSONNameProvider)
96}
97
98// Set uses the pointer to set a value from a JSON document
99func (p *Pointer) Set(document interface{}, value interface{}) (interface{}, error) {
100return document, p.set(document, value, swag.DefaultJSONNameProvider)
101}
102
103// GetForToken gets a value for a json pointer token 1 level deep
104func GetForToken(document interface{}, decodedToken string) (interface{}, reflect.Kind, error) {
105return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider)
106}
107
108// SetForToken gets a value for a json pointer token 1 level deep
109func SetForToken(document interface{}, decodedToken string, value interface{}) (interface{}, error) {
110return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider)
111}
112
113func getSingleImpl(node interface{}, decodedToken string, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
114rValue := reflect.Indirect(reflect.ValueOf(node))
115kind := rValue.Kind()
116
117if rValue.Type().Implements(jsonPointableType) {
118r, err := node.(JSONPointable).JSONLookup(decodedToken)
119if err != nil {
120return nil, kind, err
121}
122return r, kind, nil
123}
124
125switch kind {
126case reflect.Struct:
127nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
128if !ok {
129return nil, kind, fmt.Errorf("object has no field %q", decodedToken)
130}
131fld := rValue.FieldByName(nm)
132return fld.Interface(), kind, nil
133
134case reflect.Map:
135kv := reflect.ValueOf(decodedToken)
136mv := rValue.MapIndex(kv)
137
138if mv.IsValid() {
139return mv.Interface(), kind, nil
140}
141return nil, kind, fmt.Errorf("object has no key %q", decodedToken)
142
143case reflect.Slice:
144tokenIndex, err := strconv.Atoi(decodedToken)
145if err != nil {
146return nil, kind, err
147}
148sLength := rValue.Len()
149if tokenIndex < 0 || tokenIndex >= sLength {
150return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex)
151}
152
153elem := rValue.Index(tokenIndex)
154return elem.Interface(), kind, nil
155
156default:
157return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken)
158}
159
160}
161
162func setSingleImpl(node, data interface{}, decodedToken string, nameProvider *swag.NameProvider) error {
163rValue := reflect.Indirect(reflect.ValueOf(node))
164
165if ns, ok := node.(JSONSetable); ok { // pointer impl
166return ns.JSONSet(decodedToken, data)
167}
168
169if rValue.Type().Implements(jsonSetableType) {
170return node.(JSONSetable).JSONSet(decodedToken, data)
171}
172
173switch rValue.Kind() {
174case reflect.Struct:
175nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
176if !ok {
177return fmt.Errorf("object has no field %q", decodedToken)
178}
179fld := rValue.FieldByName(nm)
180if fld.IsValid() {
181fld.Set(reflect.ValueOf(data))
182}
183return nil
184
185case reflect.Map:
186kv := reflect.ValueOf(decodedToken)
187rValue.SetMapIndex(kv, reflect.ValueOf(data))
188return nil
189
190case reflect.Slice:
191tokenIndex, err := strconv.Atoi(decodedToken)
192if err != nil {
193return err
194}
195sLength := rValue.Len()
196if tokenIndex < 0 || tokenIndex >= sLength {
197return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
198}
199
200elem := rValue.Index(tokenIndex)
201if !elem.CanSet() {
202return fmt.Errorf("can't set slice index %s to %v", decodedToken, data)
203}
204elem.Set(reflect.ValueOf(data))
205return nil
206
207default:
208return fmt.Errorf("invalid token reference %q", decodedToken)
209}
210
211}
212
213func (p *Pointer) get(node interface{}, nameProvider *swag.NameProvider) (interface{}, reflect.Kind, error) {
214
215if nameProvider == nil {
216nameProvider = swag.DefaultJSONNameProvider
217}
218
219kind := reflect.Invalid
220
221// Full document when empty
222if len(p.referenceTokens) == 0 {
223return node, kind, nil
224}
225
226for _, token := range p.referenceTokens {
227
228decodedToken := Unescape(token)
229
230r, knd, err := getSingleImpl(node, decodedToken, nameProvider)
231if err != nil {
232return nil, knd, err
233}
234node, kind = r, knd
235
236}
237
238rValue := reflect.ValueOf(node)
239kind = rValue.Kind()
240
241return node, kind, nil
242}
243
244func (p *Pointer) set(node, data interface{}, nameProvider *swag.NameProvider) error {
245knd := reflect.ValueOf(node).Kind()
246
247if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array {
248return fmt.Errorf("only structs, pointers, maps and slices are supported for setting values")
249}
250
251if nameProvider == nil {
252nameProvider = swag.DefaultJSONNameProvider
253}
254
255// Full document when empty
256if len(p.referenceTokens) == 0 {
257return nil
258}
259
260lastI := len(p.referenceTokens) - 1
261for i, token := range p.referenceTokens {
262isLastToken := i == lastI
263decodedToken := Unescape(token)
264
265if isLastToken {
266
267return setSingleImpl(node, data, decodedToken, nameProvider)
268}
269
270rValue := reflect.Indirect(reflect.ValueOf(node))
271kind := rValue.Kind()
272
273if rValue.Type().Implements(jsonPointableType) {
274r, err := node.(JSONPointable).JSONLookup(decodedToken)
275if err != nil {
276return err
277}
278fld := reflect.ValueOf(r)
279if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
280node = fld.Addr().Interface()
281continue
282}
283node = r
284continue
285}
286
287switch kind {
288case reflect.Struct:
289nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken)
290if !ok {
291return fmt.Errorf("object has no field %q", decodedToken)
292}
293fld := rValue.FieldByName(nm)
294if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr {
295node = fld.Addr().Interface()
296continue
297}
298node = fld.Interface()
299
300case reflect.Map:
301kv := reflect.ValueOf(decodedToken)
302mv := rValue.MapIndex(kv)
303
304if !mv.IsValid() {
305return fmt.Errorf("object has no key %q", decodedToken)
306}
307if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr {
308node = mv.Addr().Interface()
309continue
310}
311node = mv.Interface()
312
313case reflect.Slice:
314tokenIndex, err := strconv.Atoi(decodedToken)
315if err != nil {
316return err
317}
318sLength := rValue.Len()
319if tokenIndex < 0 || tokenIndex >= sLength {
320return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex)
321}
322
323elem := rValue.Index(tokenIndex)
324if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr {
325node = elem.Addr().Interface()
326continue
327}
328node = elem.Interface()
329
330default:
331return fmt.Errorf("invalid token reference %q", decodedToken)
332}
333
334}
335
336return nil
337}
338
339// DecodedTokens returns the decoded tokens
340func (p *Pointer) DecodedTokens() []string {
341result := make([]string, 0, len(p.referenceTokens))
342for _, t := range p.referenceTokens {
343result = append(result, Unescape(t))
344}
345return result
346}
347
348// IsEmpty returns true if this is an empty json pointer
349// this indicates that it points to the root document
350func (p *Pointer) IsEmpty() bool {
351return len(p.referenceTokens) == 0
352}
353
354// Pointer to string representation function
355func (p *Pointer) String() string {
356
357if len(p.referenceTokens) == 0 {
358return emptyPointer
359}
360
361pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator)
362
363return pointerString
364}
365
366// Specific JSON pointer encoding here
367// ~0 => ~
368// ~1 => /
369// ... and vice versa
370
371const (
372encRefTok0 = `~0`
373encRefTok1 = `~1`
374decRefTok0 = `~`
375decRefTok1 = `/`
376)
377
378// Unescape unescapes a json pointer reference token string to the original representation
379func Unescape(token string) string {
380step1 := strings.Replace(token, encRefTok1, decRefTok1, -1)
381step2 := strings.Replace(step1, encRefTok0, decRefTok0, -1)
382return step2
383}
384
385// Escape escapes a pointer reference token string
386func Escape(token string) string {
387step1 := strings.Replace(token, decRefTok0, encRefTok0, -1)
388step2 := strings.Replace(step1, decRefTok1, encRefTok1, -1)
389return step2
390}
391