mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 03:41:01 +00:00
Remove ghodss/yaml (#384)
This commit is contained in:
parent
7fb9191cce
commit
ffed327564
11 changed files with 20 additions and 991 deletions
1
go.mod
1
go.mod
|
@ -17,7 +17,6 @@ require (
|
||||||
github.com/drone/envsubst v1.0.3
|
github.com/drone/envsubst v1.0.3
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||||
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf
|
github.com/franela/goblin v0.0.0-20211003143422-0a4f594942bf
|
||||||
github.com/ghodss/yaml v1.0.0
|
|
||||||
github.com/gin-gonic/gin v1.7.4
|
github.com/gin-gonic/gin v1.7.4
|
||||||
github.com/go-playground/validator/v10 v10.9.0 // indirect
|
github.com/go-playground/validator/v10 v10.9.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -417,7 +417,6 @@ github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
|
||||||
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
|
|
@ -6,7 +6,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
json "github.com/ghodss/yaml"
|
json "github.com/woodpecker-ci/woodpecker/shared/yml"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ func paramsToEnv(from map[string]interface{}, to map[string]string) error {
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
yml, _ := yaml.Marshal(vv.Interface())
|
yml, _ := yaml.Marshal(vv.Interface())
|
||||||
out, _ := json.YAMLToJSON(yml)
|
out, _ := json.Yml2Json(yml)
|
||||||
to[k] = string(out)
|
to[k] = string(out)
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
|
@ -52,7 +53,7 @@ func paramsToEnv(from map[string]interface{}, to map[string]string) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
to[k] = strings.Join(in, ",")
|
to[k] = strings.Join(in, ",")
|
||||||
} else {
|
} else {
|
||||||
out, err = json.YAMLToJSON(out)
|
out, err = json.Yml2Json(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,19 +50,28 @@ func convertMapI2MapS(v interface{}) interface{} {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadYmlFileAsJSON(path string) ([]byte, error) {
|
func Yml2Json(data []byte) (j []byte, err error) {
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := make(map[interface{}]interface{})
|
m := make(map[interface{}]interface{})
|
||||||
err = yaml.Unmarshal(data, &m)
|
err = yaml.Unmarshal(data, &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
j, err := json.Marshal(convertMapI2MapS(m))
|
j, err = json.Marshal(convertMapI2MapS(m))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadYmlFileAsJSON(path string) (j []byte, err error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
j, err = Yml2Json(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
20
vendor/github.com/ghodss/yaml/.gitignore
generated
vendored
20
vendor/github.com/ghodss/yaml/.gitignore
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
# OSX leaves these everywhere on SMB shares
|
|
||||||
._*
|
|
||||||
|
|
||||||
# Eclipse files
|
|
||||||
.classpath
|
|
||||||
.project
|
|
||||||
.settings/**
|
|
||||||
|
|
||||||
# Emacs save files
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Vim-related files
|
|
||||||
[._]*.s[a-w][a-z]
|
|
||||||
[._]s[a-w][a-z]
|
|
||||||
*.un~
|
|
||||||
Session.vim
|
|
||||||
.netrwhist
|
|
||||||
|
|
||||||
# Go test binaries
|
|
||||||
*.test
|
|
7
vendor/github.com/ghodss/yaml/.travis.yml
generated
vendored
7
vendor/github.com/ghodss/yaml/.travis.yml
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
script:
|
|
||||||
- go test
|
|
||||||
- go build
|
|
50
vendor/github.com/ghodss/yaml/LICENSE
generated
vendored
50
vendor/github.com/ghodss/yaml/LICENSE
generated
vendored
|
@ -1,50 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Sam Ghods
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
121
vendor/github.com/ghodss/yaml/README.md
generated
vendored
121
vendor/github.com/ghodss/yaml/README.md
generated
vendored
|
@ -1,121 +0,0 @@
|
||||||
# YAML marshaling and unmarshaling support for Go
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ghodss/yaml.svg)](https://travis-ci.org/ghodss/yaml)
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
|
|
||||||
|
|
||||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility).
|
|
||||||
|
|
||||||
## Caveats
|
|
||||||
|
|
||||||
**Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example:
|
|
||||||
|
|
||||||
```
|
|
||||||
BAD:
|
|
||||||
exampleKey: !!binary gIGC
|
|
||||||
|
|
||||||
GOOD:
|
|
||||||
exampleKey: gIGC
|
|
||||||
... and decode the base64 data in your code.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys.
|
|
||||||
|
|
||||||
## Installation and usage
|
|
||||||
|
|
||||||
To install, run:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go get github.com/ghodss/yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
And import using:
|
|
||||||
|
|
||||||
```
|
|
||||||
import "github.com/ghodss/yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage is very similar to the JSON library:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
Name string `json:"name"` // Affects YAML field names too.
|
|
||||||
Age int `json:"age"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Marshal a Person struct to YAML.
|
|
||||||
p := Person{"John", 30}
|
|
||||||
y, err := yaml.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(y))
|
|
||||||
/* Output:
|
|
||||||
age: 30
|
|
||||||
name: John
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Unmarshal the YAML back into a Person struct.
|
|
||||||
var p2 Person
|
|
||||||
err = yaml.Unmarshal(y, &p2)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(p2)
|
|
||||||
/* Output:
|
|
||||||
{John 30}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
j := []byte(`{"name": "John", "age": 30}`)
|
|
||||||
y, err := yaml.JSONToYAML(j)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(y))
|
|
||||||
/* Output:
|
|
||||||
name: John
|
|
||||||
age: 30
|
|
||||||
*/
|
|
||||||
j2, err := yaml.YAMLToJSON(y)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(j2))
|
|
||||||
/* Output:
|
|
||||||
{"age":30,"name":"John"}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
```
|
|
501
vendor/github.com/ghodss/yaml/fields.go
generated
vendored
501
vendor/github.com/ghodss/yaml/fields.go
generated
vendored
|
@ -1,501 +0,0 @@
|
||||||
// Copyright 2013 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
package yaml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding"
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"unicode"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
// indirect walks down v allocating pointers as needed,
|
|
||||||
// until it gets to a non-pointer.
|
|
||||||
// if it encounters an Unmarshaler, indirect stops and returns that.
|
|
||||||
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
|
|
||||||
func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
|
|
||||||
// If v is a named type and is addressable,
|
|
||||||
// start with its address, so that if the type has pointer methods,
|
|
||||||
// we find them.
|
|
||||||
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
|
|
||||||
v = v.Addr()
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
// Load value from interface, but only if the result will be
|
|
||||||
// usefully addressable.
|
|
||||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
|
||||||
e := v.Elem()
|
|
||||||
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
|
|
||||||
v = e
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Kind() != reflect.Ptr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if v.IsNil() {
|
|
||||||
if v.CanSet() {
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
} else {
|
|
||||||
v = reflect.New(v.Type().Elem())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v.Type().NumMethod() > 0 {
|
|
||||||
if u, ok := v.Interface().(json.Unmarshaler); ok {
|
|
||||||
return u, nil, reflect.Value{}
|
|
||||||
}
|
|
||||||
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
|
|
||||||
return nil, u, reflect.Value{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v = v.Elem()
|
|
||||||
}
|
|
||||||
return nil, nil, v
|
|
||||||
}
|
|
||||||
|
|
||||||
// A field represents a single field found in a struct.
|
|
||||||
type field struct {
|
|
||||||
name string
|
|
||||||
nameBytes []byte // []byte(name)
|
|
||||||
equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent
|
|
||||||
|
|
||||||
tag bool
|
|
||||||
index []int
|
|
||||||
typ reflect.Type
|
|
||||||
omitEmpty bool
|
|
||||||
quoted bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func fillField(f field) field {
|
|
||||||
f.nameBytes = []byte(f.name)
|
|
||||||
f.equalFold = foldFunc(f.nameBytes)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// byName sorts field by name, breaking ties with depth,
|
|
||||||
// then breaking ties with "name came from json tag", then
|
|
||||||
// breaking ties with index sequence.
|
|
||||||
type byName []field
|
|
||||||
|
|
||||||
func (x byName) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byName) Less(i, j int) bool {
|
|
||||||
if x[i].name != x[j].name {
|
|
||||||
return x[i].name < x[j].name
|
|
||||||
}
|
|
||||||
if len(x[i].index) != len(x[j].index) {
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
if x[i].tag != x[j].tag {
|
|
||||||
return x[i].tag
|
|
||||||
}
|
|
||||||
return byIndex(x).Less(i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// byIndex sorts field by index sequence.
|
|
||||||
type byIndex []field
|
|
||||||
|
|
||||||
func (x byIndex) Len() int { return len(x) }
|
|
||||||
|
|
||||||
func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
|
||||||
|
|
||||||
func (x byIndex) Less(i, j int) bool {
|
|
||||||
for k, xik := range x[i].index {
|
|
||||||
if k >= len(x[j].index) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if xik != x[j].index[k] {
|
|
||||||
return xik < x[j].index[k]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len(x[i].index) < len(x[j].index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeFields returns a list of fields that JSON should recognize for the given type.
|
|
||||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
|
||||||
// and then any reachable anonymous structs.
|
|
||||||
func typeFields(t reflect.Type) []field {
|
|
||||||
// Anonymous fields to explore at the current level and the next.
|
|
||||||
current := []field{}
|
|
||||||
next := []field{{typ: t}}
|
|
||||||
|
|
||||||
// Count of queued names for current level and the next.
|
|
||||||
count := map[reflect.Type]int{}
|
|
||||||
nextCount := map[reflect.Type]int{}
|
|
||||||
|
|
||||||
// Types already visited at an earlier level.
|
|
||||||
visited := map[reflect.Type]bool{}
|
|
||||||
|
|
||||||
// Fields found.
|
|
||||||
var fields []field
|
|
||||||
|
|
||||||
for len(next) > 0 {
|
|
||||||
current, next = next, current[:0]
|
|
||||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
|
||||||
|
|
||||||
for _, f := range current {
|
|
||||||
if visited[f.typ] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
visited[f.typ] = true
|
|
||||||
|
|
||||||
// Scan f.typ for fields to include.
|
|
||||||
for i := 0; i < f.typ.NumField(); i++ {
|
|
||||||
sf := f.typ.Field(i)
|
|
||||||
if sf.PkgPath != "" { // unexported
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tag := sf.Tag.Get("json")
|
|
||||||
if tag == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name, opts := parseTag(tag)
|
|
||||||
if !isValidTag(name) {
|
|
||||||
name = ""
|
|
||||||
}
|
|
||||||
index := make([]int, len(f.index)+1)
|
|
||||||
copy(index, f.index)
|
|
||||||
index[len(f.index)] = i
|
|
||||||
|
|
||||||
ft := sf.Type
|
|
||||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
|
||||||
// Follow pointer.
|
|
||||||
ft = ft.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record found field and index sequence.
|
|
||||||
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
|
||||||
tagged := name != ""
|
|
||||||
if name == "" {
|
|
||||||
name = sf.Name
|
|
||||||
}
|
|
||||||
fields = append(fields, fillField(field{
|
|
||||||
name: name,
|
|
||||||
tag: tagged,
|
|
||||||
index: index,
|
|
||||||
typ: ft,
|
|
||||||
omitEmpty: opts.Contains("omitempty"),
|
|
||||||
quoted: opts.Contains("string"),
|
|
||||||
}))
|
|
||||||
if count[f.typ] > 1 {
|
|
||||||
// If there were multiple instances, add a second,
|
|
||||||
// so that the annihilation code will see a duplicate.
|
|
||||||
// It only cares about the distinction between 1 or 2,
|
|
||||||
// so don't bother generating any more copies.
|
|
||||||
fields = append(fields, fields[len(fields)-1])
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record new anonymous struct to explore in next round.
|
|
||||||
nextCount[ft]++
|
|
||||||
if nextCount[ft] == 1 {
|
|
||||||
next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byName(fields))
|
|
||||||
|
|
||||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
|
||||||
// except that fields with JSON tags are promoted.
|
|
||||||
|
|
||||||
// The fields are sorted in primary order of name, secondary order
|
|
||||||
// of field index length. Loop over names; for each name, delete
|
|
||||||
// hidden fields by choosing the one dominant field that survives.
|
|
||||||
out := fields[:0]
|
|
||||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
|
||||||
// One iteration per name.
|
|
||||||
// Find the sequence of fields with the name of this first field.
|
|
||||||
fi := fields[i]
|
|
||||||
name := fi.name
|
|
||||||
for advance = 1; i+advance < len(fields); advance++ {
|
|
||||||
fj := fields[i+advance]
|
|
||||||
if fj.name != name {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if advance == 1 { // Only one field with this name
|
|
||||||
out = append(out, fi)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dominant, ok := dominantField(fields[i : i+advance])
|
|
||||||
if ok {
|
|
||||||
out = append(out, dominant)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = out
|
|
||||||
sort.Sort(byIndex(fields))
|
|
||||||
|
|
||||||
return fields
|
|
||||||
}
|
|
||||||
|
|
||||||
// dominantField looks through the fields, all of which are known to
|
|
||||||
// have the same name, to find the single field that dominates the
|
|
||||||
// others using Go's embedding rules, modified by the presence of
|
|
||||||
// JSON tags. If there are multiple top-level fields, the boolean
|
|
||||||
// will be false: This condition is an error in Go and we skip all
|
|
||||||
// the fields.
|
|
||||||
func dominantField(fields []field) (field, bool) {
|
|
||||||
// The fields are sorted in increasing index-length order. The winner
|
|
||||||
// must therefore be one with the shortest index length. Drop all
|
|
||||||
// longer entries, which is easy: just truncate the slice.
|
|
||||||
length := len(fields[0].index)
|
|
||||||
tagged := -1 // Index of first tagged field.
|
|
||||||
for i, f := range fields {
|
|
||||||
if len(f.index) > length {
|
|
||||||
fields = fields[:i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if f.tag {
|
|
||||||
if tagged >= 0 {
|
|
||||||
// Multiple tagged fields at the same level: conflict.
|
|
||||||
// Return no field.
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
tagged = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tagged >= 0 {
|
|
||||||
return fields[tagged], true
|
|
||||||
}
|
|
||||||
// All remaining fields have the same length. If there's more than one,
|
|
||||||
// we have a conflict (two fields named "X" at the same level) and we
|
|
||||||
// return no field.
|
|
||||||
if len(fields) > 1 {
|
|
||||||
return field{}, false
|
|
||||||
}
|
|
||||||
return fields[0], true
|
|
||||||
}
|
|
||||||
|
|
||||||
var fieldCache struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[reflect.Type][]field
|
|
||||||
}
|
|
||||||
|
|
||||||
// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
|
|
||||||
func cachedTypeFields(t reflect.Type) []field {
|
|
||||||
fieldCache.RLock()
|
|
||||||
f := fieldCache.m[t]
|
|
||||||
fieldCache.RUnlock()
|
|
||||||
if f != nil {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute fields without lock.
|
|
||||||
// Might duplicate effort but won't hold other computations back.
|
|
||||||
f = typeFields(t)
|
|
||||||
if f == nil {
|
|
||||||
f = []field{}
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldCache.Lock()
|
|
||||||
if fieldCache.m == nil {
|
|
||||||
fieldCache.m = map[reflect.Type][]field{}
|
|
||||||
}
|
|
||||||
fieldCache.m[t] = f
|
|
||||||
fieldCache.Unlock()
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidTag(s string) bool {
|
|
||||||
if s == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, c := range s {
|
|
||||||
switch {
|
|
||||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
|
||||||
// Backslash and quote chars are reserved, but
|
|
||||||
// otherwise any punctuation chars are allowed
|
|
||||||
// in a tag name.
|
|
||||||
default:
|
|
||||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
|
|
||||||
kelvin = '\u212a'
|
|
||||||
smallLongEss = '\u017f'
|
|
||||||
)
|
|
||||||
|
|
||||||
// foldFunc returns one of four different case folding equivalence
|
|
||||||
// functions, from most general (and slow) to fastest:
|
|
||||||
//
|
|
||||||
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
|
|
||||||
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
|
|
||||||
// 3) asciiEqualFold, no special, but includes non-letters (including _)
|
|
||||||
// 4) simpleLetterEqualFold, no specials, no non-letters.
|
|
||||||
//
|
|
||||||
// The letters S and K are special because they map to 3 runes, not just 2:
|
|
||||||
// * S maps to s and to U+017F 'ſ' Latin small letter long s
|
|
||||||
// * k maps to K and to U+212A 'K' Kelvin sign
|
|
||||||
// See http://play.golang.org/p/tTxjOc0OGo
|
|
||||||
//
|
|
||||||
// The returned function is specialized for matching against s and
|
|
||||||
// should only be given s. It's not curried for performance reasons.
|
|
||||||
func foldFunc(s []byte) func(s, t []byte) bool {
|
|
||||||
nonLetter := false
|
|
||||||
special := false // special letter
|
|
||||||
for _, b := range s {
|
|
||||||
if b >= utf8.RuneSelf {
|
|
||||||
return bytes.EqualFold
|
|
||||||
}
|
|
||||||
upper := b & caseMask
|
|
||||||
if upper < 'A' || upper > 'Z' {
|
|
||||||
nonLetter = true
|
|
||||||
} else if upper == 'K' || upper == 'S' {
|
|
||||||
// See above for why these letters are special.
|
|
||||||
special = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if special {
|
|
||||||
return equalFoldRight
|
|
||||||
}
|
|
||||||
if nonLetter {
|
|
||||||
return asciiEqualFold
|
|
||||||
}
|
|
||||||
return simpleLetterEqualFold
|
|
||||||
}
|
|
||||||
|
|
||||||
// equalFoldRight is a specialization of bytes.EqualFold when s is
|
|
||||||
// known to be all ASCII (including punctuation), but contains an 's',
|
|
||||||
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func equalFoldRight(s, t []byte) bool {
|
|
||||||
for _, sb := range s {
|
|
||||||
if len(t) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tb := t[0]
|
|
||||||
if tb < utf8.RuneSelf {
|
|
||||||
if sb != tb {
|
|
||||||
sbUpper := sb & caseMask
|
|
||||||
if 'A' <= sbUpper && sbUpper <= 'Z' {
|
|
||||||
if sbUpper != tb&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t = t[1:]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// sb is ASCII and t is not. t must be either kelvin
|
|
||||||
// sign or long s; sb must be s, S, k, or K.
|
|
||||||
tr, size := utf8.DecodeRune(t)
|
|
||||||
switch sb {
|
|
||||||
case 's', 'S':
|
|
||||||
if tr != smallLongEss {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case 'k', 'K':
|
|
||||||
if tr != kelvin {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
t = t[size:]
|
|
||||||
|
|
||||||
}
|
|
||||||
if len(t) > 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
|
||||||
// s is all ASCII (but may contain non-letters) and contains no
|
|
||||||
// special-folding letters.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func asciiEqualFold(s, t []byte) bool {
|
|
||||||
if len(s) != len(t) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, sb := range s {
|
|
||||||
tb := t[i]
|
|
||||||
if sb == tb {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
|
|
||||||
if sb&caseMask != tb&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
|
|
||||||
// use when s is all ASCII letters (no underscores, etc) and also
|
|
||||||
// doesn't contain 'k', 'K', 's', or 'S'.
|
|
||||||
// See comments on foldFunc.
|
|
||||||
func simpleLetterEqualFold(s, t []byte) bool {
|
|
||||||
if len(s) != len(t) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, b := range s {
|
|
||||||
if b&caseMask != t[i]&caseMask {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// tagOptions is the string following a comma in a struct field's "json"
|
|
||||||
// tag, or the empty string. It does not include the leading comma.
|
|
||||||
type tagOptions string
|
|
||||||
|
|
||||||
// parseTag splits a struct field's json tag into its name and
|
|
||||||
// comma-separated options.
|
|
||||||
func parseTag(tag string) (string, tagOptions) {
|
|
||||||
if idx := strings.Index(tag, ","); idx != -1 {
|
|
||||||
return tag[:idx], tagOptions(tag[idx+1:])
|
|
||||||
}
|
|
||||||
return tag, tagOptions("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains reports whether a comma-separated list of options
|
|
||||||
// contains a particular substr flag. substr must be surrounded by a
|
|
||||||
// string boundary or commas.
|
|
||||||
func (o tagOptions) Contains(optionName string) bool {
|
|
||||||
if len(o) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
s := string(o)
|
|
||||||
for s != "" {
|
|
||||||
var next string
|
|
||||||
i := strings.Index(s, ",")
|
|
||||||
if i >= 0 {
|
|
||||||
s, next = s[:i], s[i+1:]
|
|
||||||
}
|
|
||||||
if s == optionName {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
s = next
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
277
vendor/github.com/ghodss/yaml/yaml.go
generated
vendored
277
vendor/github.com/ghodss/yaml/yaml.go
generated
vendored
|
@ -1,277 +0,0 @@
|
||||||
package yaml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Marshals the object into JSON then converts JSON to YAML and returns the
|
|
||||||
// YAML.
|
|
||||||
func Marshal(o interface{}) ([]byte, error) {
|
|
||||||
j, err := json.Marshal(o)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error marshaling into JSON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
y, err := JSONToYAML(j)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting JSON to YAML: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return y, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts YAML to JSON then uses JSON to unmarshal into an object.
|
|
||||||
func Unmarshal(y []byte, o interface{}) error {
|
|
||||||
vo := reflect.ValueOf(o)
|
|
||||||
j, err := yamlToJSON(y, &vo)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error converting YAML to JSON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(j, o)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error unmarshaling JSON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert JSON to YAML.
|
|
||||||
func JSONToYAML(j []byte) ([]byte, error) {
|
|
||||||
// Convert the JSON to an object.
|
|
||||||
var jsonObj interface{}
|
|
||||||
// We are using yaml.Unmarshal here (instead of json.Unmarshal) because the
|
|
||||||
// Go JSON library doesn't try to pick the right number type (int, float,
|
|
||||||
// etc.) when unmarshalling to interface{}, it just picks float64
|
|
||||||
// universally. go-yaml does go through the effort of picking the right
|
|
||||||
// number type, so we can preserve number type throughout this process.
|
|
||||||
err := yaml.Unmarshal(j, &jsonObj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal this object into YAML.
|
|
||||||
return yaml.Marshal(jsonObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert YAML to JSON. Since JSON is a subset of YAML, passing JSON through
|
|
||||||
// this method should be a no-op.
|
|
||||||
//
|
|
||||||
// Things YAML can do that are not supported by JSON:
|
|
||||||
// * In YAML you can have binary and null keys in your maps. These are invalid
|
|
||||||
// in JSON. (int and float keys are converted to strings.)
|
|
||||||
// * Binary data in YAML with the !!binary tag is not supported. If you want to
|
|
||||||
// use binary data with this library, encode the data as base64 as usual but do
|
|
||||||
// not use the !!binary tag in your YAML. This will ensure the original base64
|
|
||||||
// encoded data makes it all the way through to the JSON.
|
|
||||||
func YAMLToJSON(y []byte) ([]byte, error) {
|
|
||||||
return yamlToJSON(y, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func yamlToJSON(y []byte, jsonTarget *reflect.Value) ([]byte, error) {
|
|
||||||
// Convert the YAML to an object.
|
|
||||||
var yamlObj interface{}
|
|
||||||
err := yaml.Unmarshal(y, &yamlObj)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// YAML objects are not completely compatible with JSON objects (e.g. you
|
|
||||||
// can have non-string keys in YAML). So, convert the YAML-compatible object
|
|
||||||
// to a JSON-compatible object, failing with an error if irrecoverable
|
|
||||||
// incompatibilties happen along the way.
|
|
||||||
jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert this object to JSON and return the data.
|
|
||||||
return json.Marshal(jsonObj)
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Resolve jsonTarget to a concrete value (i.e. not a pointer or an
|
|
||||||
// interface). We pass decodingNull as false because we're not actually
|
|
||||||
// decoding into the value, we're just checking if the ultimate target is a
|
|
||||||
// string.
|
|
||||||
if jsonTarget != nil {
|
|
||||||
ju, tu, pv := indirect(*jsonTarget, false)
|
|
||||||
// We have a JSON or Text Umarshaler at this level, so we can't be trying
|
|
||||||
// to decode into a string.
|
|
||||||
if ju != nil || tu != nil {
|
|
||||||
jsonTarget = nil
|
|
||||||
} else {
|
|
||||||
jsonTarget = &pv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If yamlObj is a number or a boolean, check if jsonTarget is a string -
|
|
||||||
// if so, coerce. Else return normal.
|
|
||||||
// If yamlObj is a map or array, find the field that each key is
|
|
||||||
// unmarshaling to, and when you recurse pass the reflect.Value for that
|
|
||||||
// field back into this function.
|
|
||||||
switch typedYAMLObj := yamlObj.(type) {
|
|
||||||
case map[interface{}]interface{}:
|
|
||||||
// JSON does not support arbitrary keys in a map, so we must convert
|
|
||||||
// these keys to strings.
|
|
||||||
//
|
|
||||||
// From my reading of go-yaml v2 (specifically the resolve function),
|
|
||||||
// keys can only have the types string, int, int64, float64, binary
|
|
||||||
// (unsupported), or null (unsupported).
|
|
||||||
strMap := make(map[string]interface{})
|
|
||||||
for k, v := range typedYAMLObj {
|
|
||||||
// Resolve the key to a string first.
|
|
||||||
var keyString string
|
|
||||||
switch typedKey := k.(type) {
|
|
||||||
case string:
|
|
||||||
keyString = typedKey
|
|
||||||
case int:
|
|
||||||
keyString = strconv.Itoa(typedKey)
|
|
||||||
case int64:
|
|
||||||
// go-yaml will only return an int64 as a key if the system
|
|
||||||
// architecture is 32-bit and the key's value is between 32-bit
|
|
||||||
// and 64-bit. Otherwise the key type will simply be int.
|
|
||||||
keyString = strconv.FormatInt(typedKey, 10)
|
|
||||||
case float64:
|
|
||||||
// Stolen from go-yaml to use the same conversion to string as
|
|
||||||
// the go-yaml library uses to convert float to string when
|
|
||||||
// Marshaling.
|
|
||||||
s := strconv.FormatFloat(typedKey, 'g', -1, 32)
|
|
||||||
switch s {
|
|
||||||
case "+Inf":
|
|
||||||
s = ".inf"
|
|
||||||
case "-Inf":
|
|
||||||
s = "-.inf"
|
|
||||||
case "NaN":
|
|
||||||
s = ".nan"
|
|
||||||
}
|
|
||||||
keyString = s
|
|
||||||
case bool:
|
|
||||||
if typedKey {
|
|
||||||
keyString = "true"
|
|
||||||
} else {
|
|
||||||
keyString = "false"
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Unsupported map key of type: %s, key: %+#v, value: %+#v",
|
|
||||||
reflect.TypeOf(k), k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// jsonTarget should be a struct or a map. If it's a struct, find
|
|
||||||
// the field it's going to map to and pass its reflect.Value. If
|
|
||||||
// it's a map, find the element type of the map and pass the
|
|
||||||
// reflect.Value created from that type. If it's neither, just pass
|
|
||||||
// nil - JSON conversion will error for us if it's a real issue.
|
|
||||||
if jsonTarget != nil {
|
|
||||||
t := *jsonTarget
|
|
||||||
if t.Kind() == reflect.Struct {
|
|
||||||
keyBytes := []byte(keyString)
|
|
||||||
// Find the field that the JSON library would use.
|
|
||||||
var f *field
|
|
||||||
fields := cachedTypeFields(t.Type())
|
|
||||||
for i := range fields {
|
|
||||||
ff := &fields[i]
|
|
||||||
if bytes.Equal(ff.nameBytes, keyBytes) {
|
|
||||||
f = ff
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Do case-insensitive comparison.
|
|
||||||
if f == nil && ff.equalFold(ff.nameBytes, keyBytes) {
|
|
||||||
f = ff
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f != nil {
|
|
||||||
// Find the reflect.Value of the most preferential
|
|
||||||
// struct field.
|
|
||||||
jtf := t.Field(f.index[0])
|
|
||||||
strMap[keyString], err = convertToJSONableObject(v, &jtf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if t.Kind() == reflect.Map {
|
|
||||||
// Create a zero value of the map's element type to use as
|
|
||||||
// the JSON target.
|
|
||||||
jtv := reflect.Zero(t.Type().Elem())
|
|
||||||
strMap[keyString], err = convertToJSONableObject(v, &jtv)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strMap[keyString], err = convertToJSONableObject(v, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strMap, nil
|
|
||||||
case []interface{}:
|
|
||||||
// We need to recurse into arrays in case there are any
|
|
||||||
// map[interface{}]interface{}'s inside and to convert any
|
|
||||||
// numbers to strings.
|
|
||||||
|
|
||||||
// If jsonTarget is a slice (which it really should be), find the
|
|
||||||
// thing it's going to map to. If it's not a slice, just pass nil
|
|
||||||
// - JSON conversion will error for us if it's a real issue.
|
|
||||||
var jsonSliceElemValue *reflect.Value
|
|
||||||
if jsonTarget != nil {
|
|
||||||
t := *jsonTarget
|
|
||||||
if t.Kind() == reflect.Slice {
|
|
||||||
// By default slices point to nil, but we need a reflect.Value
|
|
||||||
// pointing to a value of the slice type, so we create one here.
|
|
||||||
ev := reflect.Indirect(reflect.New(t.Type().Elem()))
|
|
||||||
jsonSliceElemValue = &ev
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make and use a new array.
|
|
||||||
arr := make([]interface{}, len(typedYAMLObj))
|
|
||||||
for i, v := range typedYAMLObj {
|
|
||||||
arr[i], err = convertToJSONableObject(v, jsonSliceElemValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr, nil
|
|
||||||
default:
|
|
||||||
// If the target type is a string and the YAML type is a number,
|
|
||||||
// convert the YAML type to a string.
|
|
||||||
if jsonTarget != nil && (*jsonTarget).Kind() == reflect.String {
|
|
||||||
// Based on my reading of go-yaml, it may return int, int64,
|
|
||||||
// float64, or uint64.
|
|
||||||
var s string
|
|
||||||
switch typedVal := typedYAMLObj.(type) {
|
|
||||||
case int:
|
|
||||||
s = strconv.FormatInt(int64(typedVal), 10)
|
|
||||||
case int64:
|
|
||||||
s = strconv.FormatInt(typedVal, 10)
|
|
||||||
case float64:
|
|
||||||
s = strconv.FormatFloat(typedVal, 'g', -1, 32)
|
|
||||||
case uint64:
|
|
||||||
s = strconv.FormatUint(typedVal, 10)
|
|
||||||
case bool:
|
|
||||||
if typedVal {
|
|
||||||
s = "true"
|
|
||||||
} else {
|
|
||||||
s = "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(s) > 0 {
|
|
||||||
yamlObj = interface{}(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return yamlObj, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -130,9 +130,6 @@ github.com/franela/goblin
|
||||||
github.com/fsnotify/fsnotify
|
github.com/fsnotify/fsnotify
|
||||||
# github.com/fzipp/gocyclo v0.3.1
|
# github.com/fzipp/gocyclo v0.3.1
|
||||||
github.com/fzipp/gocyclo
|
github.com/fzipp/gocyclo
|
||||||
# github.com/ghodss/yaml v1.0.0
|
|
||||||
## explicit
|
|
||||||
github.com/ghodss/yaml
|
|
||||||
# github.com/gin-contrib/sse v0.1.0
|
# github.com/gin-contrib/sse v0.1.0
|
||||||
github.com/gin-contrib/sse
|
github.com/gin-contrib/sse
|
||||||
# github.com/gin-gonic/gin v1.7.4
|
# github.com/gin-gonic/gin v1.7.4
|
||||||
|
|
Loading…
Reference in a new issue