forked from LaconicNetwork/kompose
This adds github.com/pkg/errors to glide.yaml followed by glide and glide-vc commands. The github.com/pkg/errors package is currently required mainly for the errors.Wrap() and errors.New() methods, since this lets us to annotate the errors while passing the error message up the call stack.
218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
// Copyright 2015 xeipuuv ( https://github.com/xeipuuv )
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// author xeipuuv
|
|
// author-github https://github.com/xeipuuv
|
|
// author-mail xeipuuv@gmail.com
|
|
//
|
|
// repository-name gojsonpointer
|
|
// repository-desc An implementation of JSON Pointer - Go language
|
|
//
|
|
// description Main and unique file.
|
|
//
|
|
// created 25-02-2013
|
|
|
|
package gojsonpointer
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
const_empty_pointer = ``
|
|
const_pointer_separator = `/`
|
|
|
|
const_invalid_start = `JSON pointer must be empty or start with a "` + const_pointer_separator + `"`
|
|
)
|
|
|
|
type implStruct struct {
|
|
mode string // "SET" or "GET"
|
|
|
|
inDocument interface{}
|
|
|
|
setInValue interface{}
|
|
|
|
getOutNode interface{}
|
|
getOutKind reflect.Kind
|
|
outError error
|
|
}
|
|
|
|
func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
|
|
|
|
var p JsonPointer
|
|
err := p.parse(jsonPointerString)
|
|
return p, err
|
|
|
|
}
|
|
|
|
type JsonPointer struct {
|
|
referenceTokens []string
|
|
}
|
|
|
|
// "Constructor", parses the given string JSON pointer
|
|
func (p *JsonPointer) parse(jsonPointerString string) error {
|
|
|
|
var err error
|
|
|
|
if jsonPointerString != const_empty_pointer {
|
|
if !strings.HasPrefix(jsonPointerString, const_pointer_separator) {
|
|
err = errors.New(const_invalid_start)
|
|
} else {
|
|
referenceTokens := strings.Split(jsonPointerString, const_pointer_separator)
|
|
for _, referenceToken := range referenceTokens[1:] {
|
|
p.referenceTokens = append(p.referenceTokens, referenceToken)
|
|
}
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Uses the pointer to retrieve a value from a JSON document
|
|
func (p *JsonPointer) Get(document interface{}) (interface{}, reflect.Kind, error) {
|
|
|
|
is := &implStruct{mode: "GET", inDocument: document}
|
|
p.implementation(is)
|
|
return is.getOutNode, is.getOutKind, is.outError
|
|
|
|
}
|
|
|
|
// Uses the pointer to update a value from a JSON document
|
|
func (p *JsonPointer) Set(document interface{}, value interface{}) (interface{}, error) {
|
|
|
|
is := &implStruct{mode: "SET", inDocument: document, setInValue: value}
|
|
p.implementation(is)
|
|
return document, is.outError
|
|
|
|
}
|
|
|
|
// Both Get and Set functions use the same implementation to avoid code duplication
|
|
func (p *JsonPointer) implementation(i *implStruct) {
|
|
|
|
kind := reflect.Invalid
|
|
|
|
// Full document when empty
|
|
if len(p.referenceTokens) == 0 {
|
|
i.getOutNode = i.inDocument
|
|
i.outError = nil
|
|
i.getOutKind = kind
|
|
i.outError = nil
|
|
return
|
|
}
|
|
|
|
node := i.inDocument
|
|
|
|
for ti, token := range p.referenceTokens {
|
|
|
|
decodedToken := decodeReferenceToken(token)
|
|
isLastToken := ti == len(p.referenceTokens)-1
|
|
|
|
rValue := reflect.ValueOf(node)
|
|
kind = rValue.Kind()
|
|
|
|
switch kind {
|
|
|
|
case reflect.Map:
|
|
m := node.(map[string]interface{})
|
|
if _, ok := m[decodedToken]; ok {
|
|
node = m[decodedToken]
|
|
if isLastToken && i.mode == "SET" {
|
|
m[decodedToken] = i.setInValue
|
|
}
|
|
} else {
|
|
i.outError = errors.New(fmt.Sprintf("Object has no key '%s'", token))
|
|
i.getOutKind = kind
|
|
i.getOutNode = nil
|
|
return
|
|
}
|
|
|
|
case reflect.Slice:
|
|
s := node.([]interface{})
|
|
tokenIndex, err := strconv.Atoi(token)
|
|
if err != nil {
|
|
i.outError = errors.New(fmt.Sprintf("Invalid array index '%s'", token))
|
|
i.getOutKind = kind
|
|
i.getOutNode = nil
|
|
return
|
|
}
|
|
sLength := len(s)
|
|
if tokenIndex < 0 || tokenIndex >= sLength {
|
|
i.outError = errors.New(fmt.Sprintf("Out of bound array[0,%d] index '%d'", sLength, tokenIndex))
|
|
i.getOutKind = kind
|
|
i.getOutNode = nil
|
|
return
|
|
}
|
|
|
|
node = s[tokenIndex]
|
|
if isLastToken && i.mode == "SET" {
|
|
s[tokenIndex] = i.setInValue
|
|
}
|
|
|
|
default:
|
|
i.outError = errors.New(fmt.Sprintf("Invalid token reference '%s'", token))
|
|
i.getOutKind = kind
|
|
i.getOutNode = nil
|
|
return
|
|
}
|
|
|
|
}
|
|
|
|
rValue := reflect.ValueOf(node)
|
|
kind = rValue.Kind()
|
|
|
|
i.getOutNode = node
|
|
i.getOutKind = kind
|
|
i.outError = nil
|
|
}
|
|
|
|
// Pointer to string representation function
|
|
func (p *JsonPointer) String() string {
|
|
|
|
if len(p.referenceTokens) == 0 {
|
|
return const_empty_pointer
|
|
}
|
|
|
|
pointerString := const_pointer_separator + strings.Join(p.referenceTokens, const_pointer_separator)
|
|
|
|
return pointerString
|
|
}
|
|
|
|
// Specific JSON pointer encoding here
|
|
// ~0 => ~
|
|
// ~1 => /
|
|
// ... and vice versa
|
|
|
|
const (
|
|
const_encoded_reference_token_0 = `~0`
|
|
const_encoded_reference_token_1 = `~1`
|
|
const_decoded_reference_token_0 = `~`
|
|
const_decoded_reference_token_1 = `/`
|
|
)
|
|
|
|
func decodeReferenceToken(token string) string {
|
|
step1 := strings.Replace(token, const_encoded_reference_token_1, const_decoded_reference_token_1, -1)
|
|
step2 := strings.Replace(step1, const_encoded_reference_token_0, const_decoded_reference_token_0, -1)
|
|
return step2
|
|
}
|
|
|
|
func encodeReferenceToken(token string) string {
|
|
step1 := strings.Replace(token, const_decoded_reference_token_1, const_encoded_reference_token_1, -1)
|
|
step2 := strings.Replace(step1, const_decoded_reference_token_0, const_encoded_reference_token_0, -1)
|
|
return step2
|
|
}
|