[WIP] Initial commit
This commit is contained in:
0
vendor/github.com/hjson/hjson-go/v4/.gitignore
generated
vendored
Normal file
0
vendor/github.com/hjson/hjson-go/v4/.gitignore
generated
vendored
Normal file
21
vendor/github.com/hjson/hjson-go/v4/LICENSE
generated
vendored
Normal file
21
vendor/github.com/hjson/hjson-go/v4/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016, 2017 Christian Zangl
|
||||
|
||||
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.
|
||||
377
vendor/github.com/hjson/hjson-go/v4/README.md
generated
vendored
Normal file
377
vendor/github.com/hjson/hjson-go/v4/README.md
generated
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
# hjson-go
|
||||
|
||||
[](https://github.com/hjson/hjson-go/actions)
|
||||
[](https://github.com/hjson/hjson-go/releases)
|
||||
[](https://goreportcard.com/report/github.com/hjson/hjson-go)
|
||||
[](https://gocover.io/github.com/hjson/hjson-go/)
|
||||
[](https://godoc.org/github.com/hjson/hjson-go/v4)
|
||||
|
||||

|
||||
|
||||
```
|
||||
{
|
||||
# specify rate in requests/second (because comments are helpful!)
|
||||
rate: 1000
|
||||
|
||||
// prefer c-style comments?
|
||||
/* feeling old fashioned? */
|
||||
|
||||
# did you notice that rate doesn't need quotes?
|
||||
hey: look ma, no quotes for strings either!
|
||||
|
||||
# best of all
|
||||
notice: []
|
||||
anything: ?
|
||||
|
||||
# yes, commas are optional!
|
||||
}
|
||||
```
|
||||
|
||||
The Go implementation of Hjson is based on [hjson-js](https://github.com/hjson/hjson-js). For other platforms see [hjson.github.io](https://hjson.github.io).
|
||||
|
||||
More documentation can be found at https://pkg.go.dev/github.com/hjson/hjson-go/v4
|
||||
|
||||
# Install
|
||||
|
||||
Instructions for installing a pre-built **hjson-cli** tool can be found at https://hjson.github.io/users-bin.html
|
||||
|
||||
If you instead want to build locally, make sure you have a working Go environment. See the [install instructions](https://golang.org/doc/install.html).
|
||||
|
||||
- In order to use Hjson from your own Go source code, just add an import line like the one here below. Before building your project, run `go mod tidy` in order to download the Hjson source files. The suffix `/v4` is required in the import path, unless you specifically want to use an older major version.
|
||||
```go
|
||||
import "github.com/hjson/hjson-go/v4"
|
||||
```
|
||||
- If you instead want to use the **hjson-cli** command line tool, run the command here below in your terminal. The executable will be installed into your `go/bin` folder, make sure that folder is included in your `PATH` environment variable.
|
||||
```bash
|
||||
go install github.com/hjson/hjson-go/v4/hjson-cli@latest
|
||||
```
|
||||
# Usage as command line tool
|
||||
```
|
||||
usage: hjson-cli [OPTIONS] [INPUT]
|
||||
hjson can be used to convert JSON from/to Hjson.
|
||||
|
||||
hjson will read the given JSON/Hjson input file or read from stdin.
|
||||
|
||||
Options:
|
||||
-bracesSameLine
|
||||
Print braces on the same line.
|
||||
-c Output as JSON.
|
||||
-h Show this screen.
|
||||
-indentBy string
|
||||
The indent string. (default " ")
|
||||
-j Output as formatted JSON.
|
||||
-omitRootBraces
|
||||
Omit braces at the root.
|
||||
-preserveKeyOrder
|
||||
Preserve key order in objects/maps.
|
||||
-quoteAlways
|
||||
Always quote string values.
|
||||
-v
|
||||
Show version.
|
||||
```
|
||||
|
||||
Sample:
|
||||
- run `hjson-cli test.json > test.hjson` to convert to Hjson
|
||||
- run `hjson-cli -j test.hjson > test.json` to convert to JSON
|
||||
|
||||
# Usage as a GO library
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hjson/hjson-go/v4"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Now let's look at decoding Hjson data into Go
|
||||
// values.
|
||||
sampleText := []byte(`
|
||||
{
|
||||
# specify rate in requests/second
|
||||
rate: 1000
|
||||
array:
|
||||
[
|
||||
foo
|
||||
bar
|
||||
]
|
||||
}`)
|
||||
|
||||
// We need to provide a variable where Hjson
|
||||
// can put the decoded data.
|
||||
var dat map[string]interface{}
|
||||
|
||||
// Decode with default options and check for errors.
|
||||
if err := hjson.Unmarshal(sampleText, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// short for:
|
||||
// options := hjson.DefaultDecoderOptions()
|
||||
// err := hjson.UnmarshalWithOptions(sampleText, &dat, options)
|
||||
fmt.Println(dat)
|
||||
|
||||
// In order to use the values in the decoded map,
|
||||
// we'll need to cast them to their appropriate type.
|
||||
|
||||
rate := dat["rate"].(float64)
|
||||
fmt.Println(rate)
|
||||
|
||||
array := dat["array"].([]interface{})
|
||||
str1 := array[0].(string)
|
||||
fmt.Println(str1)
|
||||
|
||||
|
||||
// To encode to Hjson with default options:
|
||||
sampleMap := map[string]int{"apple": 5, "lettuce": 7}
|
||||
hjson, _ := hjson.Marshal(sampleMap)
|
||||
// short for:
|
||||
// options := hjson.DefaultOptions()
|
||||
// hjson, _ := hjson.MarshalWithOptions(sampleMap, options)
|
||||
fmt.Println(string(hjson))
|
||||
}
|
||||
```
|
||||
|
||||
## Unmarshal to Go structs
|
||||
|
||||
If you prefer, you can also unmarshal to Go structs (including structs implementing the json.Unmarshaler interface or the encoding.TextUnmarshaler interface). The Go JSON package is used for this, so the same rules apply. Specifically for the "json" key in struct field tags. For more details about this type of unmarshalling, see the [documentation for json.Unmarshal()](https://pkg.go.dev/encoding/json#Unmarshal).
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hjson/hjson-go/v4"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Sample struct {
|
||||
Rate int
|
||||
Array []string
|
||||
}
|
||||
|
||||
type SampleAlias struct {
|
||||
Rett int `json:"rate"`
|
||||
Ashtray []string `json:"array"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
sampleText := []byte(`
|
||||
{
|
||||
# specify rate in requests/second
|
||||
rate: 1000
|
||||
array:
|
||||
[
|
||||
foo
|
||||
bar
|
||||
]
|
||||
}`)
|
||||
|
||||
// unmarshal
|
||||
var sample Sample
|
||||
hjson.Unmarshal(sampleText, &sample)
|
||||
|
||||
fmt.Println(sample.Rate)
|
||||
fmt.Println(sample.Array)
|
||||
|
||||
// unmarshal using json tags on struct fields
|
||||
var sampleAlias SampleAlias
|
||||
hjson.Unmarshal(sampleText, &sampleAlias)
|
||||
|
||||
fmt.Println(sampleAlias.Rett)
|
||||
fmt.Println(sampleAlias.Ashtray)
|
||||
}
|
||||
```
|
||||
|
||||
## Comments on struct fields
|
||||
|
||||
By using key `comment` in struct field tags you can specify comments to be written on one or more lines preceding the struct field in the Hjson output. Another way to output comments is to use *hjson.Node* structs, more on than later.
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hjson/hjson-go/v4"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type foo struct {
|
||||
A string `json:"x" comment:"First comment"`
|
||||
B int32 `comment:"Second comment\nLook ma, new lines"`
|
||||
C string
|
||||
D int32
|
||||
}
|
||||
|
||||
func main() {
|
||||
a := foo{A: "hi!", B: 3, C: "some text", D: 5}
|
||||
buf, err := hjson.Marshal(a)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(buf))
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
{
|
||||
# First comment
|
||||
x: hi!
|
||||
|
||||
# Second comment
|
||||
# Look ma, new lines
|
||||
B: 3
|
||||
|
||||
C: some text
|
||||
D: 5
|
||||
}
|
||||
```
|
||||
|
||||
## Read and write comments
|
||||
|
||||
The only way to read comments from Hjson input is to use a destination variable of type *hjson.Node* or **hjson.Node*. The *hjson.Node* must be the root destination, it won't work if you create a field of type *hjson.Node* in some other struct and use that struct as destination. An *hjson.Node* struct is simply a wrapper for a value and comments stored in an *hjson.Comments* struct. It also has several convenience functions, for example *AtIndex()* or *SetKey()* that can be used when you know that the node contains a value of type `[]interface{}` or **hjson.OrderedMap*. All of the elements in `[]interface{}` or **hjson.OrderedMap* will be of type **hjson.Node* in trees created by *hjson.Unmarshal*, but the *hjson.Node* convenience functions unpack the actual values from them.
|
||||
|
||||
When *hjson.Node* or **hjson.Node* is used as destination for Hjson unmarshal the output will be a tree of **hjson.Node* where all of the values contained in tree nodes will be of these types:
|
||||
|
||||
* `nil` (no type)
|
||||
* `float64` (if *UseJSONNumber* == `false`)
|
||||
* *json.Number* (if *UseJSONNumber* == `true`)
|
||||
* `string`
|
||||
* `bool`
|
||||
* `[]interface{}`
|
||||
* **hjson.OrderedMap*
|
||||
|
||||
These are just the types used by Hjson unmarshal and the convenience functions, you are free to assign any type of values to nodes in your own code.
|
||||
|
||||
The comments will contain all whitespace chars too (including line feeds) so that an Hjson document can be read and written without altering the layout. This can be disabled by setting the decoding option *WhitespaceAsComments* to `false`.
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hjson/hjson-go/v4"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Now let's look at decoding Hjson data into hjson.Node.
|
||||
sampleText := []byte(`
|
||||
{
|
||||
# specify rate in requests/second
|
||||
rate: 1000
|
||||
array:
|
||||
[
|
||||
foo
|
||||
bar
|
||||
]
|
||||
}`)
|
||||
|
||||
var node hjson.Node
|
||||
if err := hjson.Unmarshal(sampleText, &node); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
node.NK("array").Cm.Before = ` # please specify an array
|
||||
`
|
||||
|
||||
if _, _, err := node.NKC("subMap").SetKey("subVal", 1); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outBytes, err := hjson.Marshal(node)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(string(outBytes))
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
|
||||
{
|
||||
# specify rate in requests/second
|
||||
rate: 1000
|
||||
# please specify an array
|
||||
array:
|
||||
[
|
||||
foo
|
||||
bar
|
||||
]
|
||||
subMap: {
|
||||
subVal: 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Type ambiguity
|
||||
|
||||
Hjson allows quoteless strings. But if a value is a valid number, boolean or `null` then it will be unmarshalled into that type instead of a string when unmarshalling into `interface{}`. This can lead to unintended consequences if the creator of an Hjson file meant to write a string but didn't think of that the quoteless string they wrote also was a valid number.
|
||||
|
||||
The ambiguity can be avoided by using typed destinations when unmarshalling. A string destination will receive a string even if the quoteless string also was a valid number, boolean or `null`. Example:
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/hjson/hjson-go/v4"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type foo struct {
|
||||
A string
|
||||
}
|
||||
|
||||
func main() {
|
||||
var dest foo
|
||||
err := hjson.Unmarshal([]byte(`a: 3`), &dest)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
fmt.Println(dest)
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
{3}
|
||||
```
|
||||
|
||||
String pointer destinations can be set to `nil` by writing `null` in an Hjson file. The same goes for a pointer destination of any type that implements `UnmarshalText()`.
|
||||
|
||||
## ElemTyper interface
|
||||
|
||||
If a destination type implements hjson.ElemTyper, Unmarshal() will call ElemType() on the destination when unmarshalling an array or an object, to see if any array element or leaf node should be of type string even if it can be treated as a number, boolean or null. This is most useful if the destination also implements the json.Unmarshaler interface, because then there is no other way for Unmarshal() to know the type of the elements on the destination. If a destination implements ElemTyper all of its elements must be of the same type.
|
||||
|
||||
Example implementation for a generic ordered map:
|
||||
|
||||
```go
|
||||
|
||||
func (o *OrderedMap[T]) ElemType() reflect.Type {
|
||||
return reflect.TypeOf((*T)(nil)).Elem()
|
||||
}
|
||||
```
|
||||
|
||||
## Max nesting depth
|
||||
|
||||
In order to avoid stack overflow all unmarshal-functions return an error if the Hjson input contains a tree more than 10000 levels deep. This is the same limit as in the Go standard library package `encoding/json`.
|
||||
|
||||
# API
|
||||
|
||||
[](https://godoc.org/github.com/hjson/hjson-go/v4)
|
||||
|
||||
# History
|
||||
|
||||
[see releases](https://github.com/hjson/hjson-go/releases)
|
||||
59
vendor/github.com/hjson/hjson-go/v4/build_release.sh
generated
vendored
Normal file
59
vendor/github.com/hjson/hjson-go/v4/build_release.sh
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd `dirname $0`
|
||||
ROOT=$PWD
|
||||
BINARIES=$ROOT/binaries
|
||||
|
||||
if [ -d "$BINARIES" ]; then rm -rf $BINARIES; fi
|
||||
|
||||
mkdir $BINARIES
|
||||
|
||||
function build() {
|
||||
export GOOS=$1
|
||||
export GOARCH=$2
|
||||
|
||||
echo build $GOOS $GOARCH
|
||||
VERSION=`cd $ROOT && git describe --tags`
|
||||
OUT=${BINARIES}/hjson_${VERSION}_${GOOS}_${GOARCH}
|
||||
mkdir $OUT
|
||||
cd $OUT
|
||||
go build -ldflags "-w -s -X main.Version=${VERSION}" github.com/hjson/hjson-go/v4/hjson-cli
|
||||
if [[ $3 == "zip" ]]; then
|
||||
mv $OUT/hjson-cli.exe $OUT/hjson.exe
|
||||
zip -j ${OUT}.zip $OUT/*
|
||||
else
|
||||
mv $OUT/hjson-cli $OUT/hjson
|
||||
tar -czf ${OUT}.tar.gz -C $OUT .
|
||||
fi
|
||||
rm -r $OUT
|
||||
|
||||
}
|
||||
|
||||
build android arm64
|
||||
build darwin amd64
|
||||
build darwin arm64
|
||||
build dragonfly amd64
|
||||
build freebsd 386
|
||||
build freebsd amd64
|
||||
build freebsd arm
|
||||
build linux 386
|
||||
build linux amd64
|
||||
build linux arm
|
||||
build linux arm64
|
||||
build linux mips64
|
||||
build linux mips64le
|
||||
build linux ppc64
|
||||
build linux ppc64le
|
||||
build netbsd 386
|
||||
build netbsd amd64
|
||||
build netbsd arm
|
||||
build openbsd 386
|
||||
build openbsd amd64
|
||||
build openbsd arm
|
||||
build plan9 386
|
||||
build plan9 amd64
|
||||
build solaris amd64
|
||||
build windows 386 zip
|
||||
build windows amd64 zip
|
||||
build windows arm zip
|
||||
build windows arm64 zip
|
||||
1057
vendor/github.com/hjson/hjson-go/v4/decode.go
generated
vendored
Normal file
1057
vendor/github.com/hjson/hjson-go/v4/decode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
709
vendor/github.com/hjson/hjson-go/v4/encode.go
generated
vendored
Normal file
709
vendor/github.com/hjson/hjson-go/v4/encode.go
generated
vendored
Normal file
@@ -0,0 +1,709 @@
|
||||
package hjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// EncoderOptions defines options for encoding to Hjson.
|
||||
type EncoderOptions struct {
|
||||
// End of line, should be either \n or \r\n
|
||||
Eol string
|
||||
// Place braces on the same line
|
||||
BracesSameLine bool
|
||||
// Emit braces for the root object
|
||||
EmitRootBraces bool
|
||||
// Always place string in quotes
|
||||
QuoteAlways bool
|
||||
// Place string in quotes if it could otherwise be a number, boolean or null
|
||||
QuoteAmbiguousStrings bool
|
||||
// Indent string
|
||||
IndentBy string
|
||||
// Base indentation string
|
||||
BaseIndentation string
|
||||
// Write comments, if any are found in hjson.Node structs or as tags on
|
||||
// other structs.
|
||||
Comments bool
|
||||
}
|
||||
|
||||
// DefaultOptions returns the default encoding options.
|
||||
// Eol = "\n"
|
||||
// BracesSameLine = true
|
||||
// EmitRootBraces = true
|
||||
// QuoteAlways = false
|
||||
// QuoteAmbiguousStrings = true
|
||||
// IndentBy = " "
|
||||
// BaseIndentation = ""
|
||||
// Comments = true
|
||||
func DefaultOptions() EncoderOptions {
|
||||
return EncoderOptions{
|
||||
Eol: "\n",
|
||||
BracesSameLine: true,
|
||||
EmitRootBraces: true,
|
||||
QuoteAlways: false,
|
||||
QuoteAmbiguousStrings: true,
|
||||
IndentBy: " ",
|
||||
BaseIndentation: "",
|
||||
Comments: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Start looking for circular references below this depth.
|
||||
const depthLimit = 1024
|
||||
|
||||
type hjsonEncoder struct {
|
||||
bytes.Buffer // output
|
||||
EncoderOptions
|
||||
indent int
|
||||
pDepth uint
|
||||
parents map[uintptr]struct{} // Starts to be filled after pDepth has reached depthLimit
|
||||
structTypeCache map[reflect.Type][]structFieldInfo
|
||||
}
|
||||
|
||||
var JSONNumberType = reflect.TypeOf(json.Number(""))
|
||||
|
||||
var needsEscape, needsQuotes, needsEscapeML, startsWithKeyword, needsEscapeName *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
var commonRange = `\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}`
|
||||
// needsEscape tests if the string can be written without escapes
|
||||
needsEscape = regexp.MustCompile(`[\\\"\x00-\x1f` + commonRange + `]`)
|
||||
// needsQuotes tests if the string can be written as a quoteless string (includes needsEscape but without \\ and \")
|
||||
needsQuotes = regexp.MustCompile(`^\s|^"|^'|^#|^/\*|^//|^\{|^\}|^\[|^\]|^:|^,|\s$|[\x00-\x1f\x7f-\x9f\x{00ad}\x{0600}-\x{0604}\x{070f}\x{17b4}\x{17b5}\x{200c}-\x{200f}\x{2028}-\x{202f}\x{2060}-\x{206f}\x{feff}\x{fff0}-\x{ffff}]`)
|
||||
// needsEscapeML tests if the string can be written as a multiline string (like needsEscape but without \n, \\, \", \t)
|
||||
needsEscapeML = regexp.MustCompile(`'''|^[\s]+$|[\x00-\x08\x0b-\x1f` + commonRange + `]`)
|
||||
// starts with a keyword and optionally is followed by a comment
|
||||
startsWithKeyword = regexp.MustCompile(`^(true|false|null)\s*((,|\]|\}|#|//|/\*).*)?$`)
|
||||
needsEscapeName = regexp.MustCompile(`[,\{\[\}\]\s:#"']|//|/\*`)
|
||||
}
|
||||
|
||||
var meta = map[byte][]byte{
|
||||
// table of character substitutions
|
||||
'\b': []byte("\\b"),
|
||||
'\t': []byte("\\t"),
|
||||
'\n': []byte("\\n"),
|
||||
'\f': []byte("\\f"),
|
||||
'\r': []byte("\\r"),
|
||||
'"': []byte("\\\""),
|
||||
'\\': []byte("\\\\"),
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) quoteReplace(text string) string {
|
||||
return string(needsEscape.ReplaceAllFunc([]byte(text), func(a []byte) []byte {
|
||||
c := meta[a[0]]
|
||||
if c != nil {
|
||||
return c
|
||||
}
|
||||
r, _ := utf8.DecodeRune(a)
|
||||
return []byte(fmt.Sprintf("\\u%04x", r))
|
||||
}))
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) quoteForComment(cmStr string) bool {
|
||||
chars := []rune(cmStr)
|
||||
for _, r := range chars {
|
||||
switch r {
|
||||
case '\r', '\n':
|
||||
return false
|
||||
case '/', '#':
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool,
|
||||
keyComment string, hasCommentAfter bool) {
|
||||
|
||||
// Check if we can insert this string without quotes
|
||||
// see hjson syntax (must not parse as true, false, null or number)
|
||||
|
||||
if len(value) == 0 {
|
||||
e.WriteString(separator + `""`)
|
||||
} else if e.QuoteAlways ||
|
||||
hasCommentAfter ||
|
||||
needsQuotes.MatchString(value) ||
|
||||
(e.QuoteAmbiguousStrings && (startsWithNumber([]byte(value)) ||
|
||||
startsWithKeyword.MatchString(value))) {
|
||||
|
||||
// If the string contains no control characters, no quote characters, and no
|
||||
// backslash characters, then we can safely slap some quotes around it.
|
||||
// Otherwise we first check if the string can be expressed in multiline
|
||||
// format or we must replace the offending characters with safe escape
|
||||
// sequences.
|
||||
|
||||
if !needsEscape.MatchString(value) {
|
||||
e.WriteString(separator + `"` + value + `"`)
|
||||
} else if !needsEscapeML.MatchString(value) && !isRootObject {
|
||||
e.mlString(value, separator, keyComment)
|
||||
} else {
|
||||
e.WriteString(separator + `"` + e.quoteReplace(value) + `"`)
|
||||
}
|
||||
} else {
|
||||
// return without quotes
|
||||
e.WriteString(separator + value)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) mlString(value string, separator string, keyComment string) {
|
||||
a := strings.Split(value, "\n")
|
||||
|
||||
if len(a) == 1 {
|
||||
// The string contains only a single line. We still use the multiline
|
||||
// format as it avoids escaping the \ character (e.g. when used in a
|
||||
// regex).
|
||||
e.WriteString(separator + "'''")
|
||||
e.WriteString(a[0])
|
||||
} else {
|
||||
if !strings.Contains(keyComment, "\n") {
|
||||
e.writeIndent(e.indent + 1)
|
||||
}
|
||||
e.WriteString("'''")
|
||||
for _, v := range a {
|
||||
indent := e.indent + 1
|
||||
if len(v) == 0 {
|
||||
indent = 0
|
||||
}
|
||||
e.writeIndent(indent)
|
||||
e.WriteString(v)
|
||||
}
|
||||
e.writeIndent(e.indent + 1)
|
||||
}
|
||||
e.WriteString("'''")
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) quoteName(name string) string {
|
||||
if len(name) == 0 {
|
||||
return `""`
|
||||
}
|
||||
|
||||
// Check if we can insert this name without quotes
|
||||
|
||||
if needsEscapeName.MatchString(name) || needsEscape.MatchString(name) {
|
||||
return `"` + e.quoteReplace(name) + `"`
|
||||
}
|
||||
// without quotes
|
||||
return name
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) bracesIndent(isObjElement, isEmpty bool, cm Comments,
|
||||
separator string) {
|
||||
|
||||
if !isObjElement || cm.Key == "" {
|
||||
if isObjElement && !e.BracesSameLine && (!isEmpty || cm.InsideFirst != "") {
|
||||
e.writeIndent(e.indent)
|
||||
} else {
|
||||
e.WriteString(separator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type sortAlpha []reflect.Value
|
||||
|
||||
func (s sortAlpha) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
func (s sortAlpha) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
func (s sortAlpha) Less(i, j int) bool {
|
||||
return fmt.Sprintf("%v", s[i]) < fmt.Sprintf("%v", s[j])
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) writeIndentNoEOL(indent int) {
|
||||
e.WriteString(e.BaseIndentation)
|
||||
for i := 0; i < indent; i++ {
|
||||
e.WriteString(e.IndentBy)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) writeIndent(indent int) {
|
||||
e.WriteString(e.Eol)
|
||||
e.writeIndentNoEOL(indent)
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) useMarshalerJSON(
|
||||
value reflect.Value,
|
||||
noIndent bool,
|
||||
separator string,
|
||||
isRootObject,
|
||||
isObjElement bool,
|
||||
) error {
|
||||
b, err := value.Interface().(json.Marshaler).MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decOpt := DefaultDecoderOptions()
|
||||
decOpt.UseJSONNumber = true
|
||||
var dummyDest interface{}
|
||||
jsonRoot, err := orderedUnmarshal(b, &dummyDest, decOpt, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Output Hjson with our current options, instead of JSON.
|
||||
return e.str(reflect.ValueOf(jsonRoot), noIndent, separator, isRootObject,
|
||||
isObjElement, Comments{})
|
||||
}
|
||||
|
||||
var marshalerJSON = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
|
||||
var marshalerText = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||
|
||||
func (e *hjsonEncoder) unpackNode(value reflect.Value, cm Comments) (reflect.Value, Comments) {
|
||||
if value.IsValid() {
|
||||
if node, ok := value.Interface().(Node); ok {
|
||||
value = reflect.ValueOf(node.Value)
|
||||
if e.Comments {
|
||||
cm = node.Cm
|
||||
}
|
||||
} else if pNode, ok := value.Interface().(*Node); ok {
|
||||
value = reflect.ValueOf(pNode.Value)
|
||||
if e.Comments {
|
||||
cm = pNode.Cm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value, cm
|
||||
}
|
||||
|
||||
// This function can often be called from within itself, so do not output
|
||||
// anything from the upper half of it.
|
||||
func (e *hjsonEncoder) str(
|
||||
value reflect.Value,
|
||||
noIndent bool,
|
||||
separator string,
|
||||
isRootObject,
|
||||
isObjElement bool,
|
||||
cm Comments,
|
||||
) error {
|
||||
|
||||
// Produce a string from value.
|
||||
|
||||
// Unpack *Node, possibly overwrite cm.
|
||||
value, cm = e.unpackNode(value, cm)
|
||||
|
||||
if cm.Key != "" {
|
||||
separator = ""
|
||||
}
|
||||
|
||||
kind := value.Kind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
if e.pDepth++; e.pDepth > depthLimit {
|
||||
if e.parents == nil {
|
||||
e.parents = map[uintptr]struct{}{}
|
||||
}
|
||||
p := value.Pointer()
|
||||
if _, ok := e.parents[p]; ok {
|
||||
return errors.New("Circular reference found, pointer of type " + value.Type().String())
|
||||
}
|
||||
e.parents[p] = struct{}{}
|
||||
defer delete(e.parents, p)
|
||||
}
|
||||
defer func() { e.pDepth-- }()
|
||||
}
|
||||
|
||||
if !value.IsValid() {
|
||||
e.WriteString(separator)
|
||||
e.WriteString("null")
|
||||
return nil
|
||||
}
|
||||
|
||||
if kind == reflect.Interface || kind == reflect.Ptr {
|
||||
if value.IsNil() {
|
||||
e.WriteString(separator)
|
||||
e.WriteString("null")
|
||||
return nil
|
||||
}
|
||||
return e.str(value.Elem(), noIndent, separator, isRootObject, isObjElement, cm)
|
||||
}
|
||||
|
||||
// Our internal orderedMap implements marshalerJSON. We must therefore place
|
||||
// this check before checking marshalerJSON. Calling orderedMap.MarshalJSON()
|
||||
// from this function would cause an infinite loop.
|
||||
if om, ok := value.Interface().(OrderedMap); ok {
|
||||
var fis []fieldInfo
|
||||
for _, key := range om.Keys {
|
||||
fis = append(fis, fieldInfo{
|
||||
field: reflect.ValueOf(om.Map[key]),
|
||||
name: key,
|
||||
})
|
||||
}
|
||||
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
|
||||
}
|
||||
|
||||
if value.Type().Implements(marshalerJSON) {
|
||||
return e.useMarshalerJSON(value, noIndent, separator, isRootObject, isObjElement)
|
||||
}
|
||||
|
||||
if value.Type().Implements(marshalerText) {
|
||||
b, err := value.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.str(reflect.ValueOf(string(b)), noIndent, separator, isRootObject,
|
||||
isObjElement, cm)
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
if value.Type() == JSONNumberType {
|
||||
n := value.String()
|
||||
if n == "" {
|
||||
n = "0"
|
||||
}
|
||||
// without quotes
|
||||
e.WriteString(separator + n)
|
||||
} else {
|
||||
e.quote(value.String(), separator, isRootObject, cm.Key,
|
||||
e.quoteForComment(cm.After))
|
||||
}
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
e.WriteString(separator)
|
||||
e.WriteString(strconv.FormatInt(value.Int(), 10))
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Uintptr:
|
||||
e.WriteString(separator)
|
||||
e.WriteString(strconv.FormatUint(value.Uint(), 10))
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
// JSON numbers must be finite. Encode non-finite numbers as null.
|
||||
e.WriteString(separator)
|
||||
number := value.Float()
|
||||
if math.IsInf(number, 0) || math.IsNaN(number) {
|
||||
e.WriteString("null")
|
||||
} else if number == -0 {
|
||||
e.WriteString("0")
|
||||
} else {
|
||||
// find shortest representation ('G' does not work)
|
||||
val := strconv.FormatFloat(number, 'f', -1, 64)
|
||||
exp := strconv.FormatFloat(number, 'E', -1, 64)
|
||||
if len(exp) < len(val) {
|
||||
val = strings.ToLower(exp)
|
||||
}
|
||||
e.WriteString(val)
|
||||
}
|
||||
|
||||
case reflect.Bool:
|
||||
e.WriteString(separator)
|
||||
if value.Bool() {
|
||||
e.WriteString("true")
|
||||
} else {
|
||||
e.WriteString("false")
|
||||
}
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
e.bracesIndent(isObjElement, value.Len() == 0, cm, separator)
|
||||
e.WriteString("[" + cm.InsideFirst)
|
||||
|
||||
if value.Len() == 0 {
|
||||
if cm.InsideFirst != "" || cm.InsideLast != "" {
|
||||
e.WriteString(e.Eol)
|
||||
if cm.InsideLast == "" {
|
||||
e.writeIndentNoEOL(e.indent)
|
||||
}
|
||||
}
|
||||
e.WriteString(cm.InsideLast + "]")
|
||||
return nil
|
||||
}
|
||||
|
||||
indent1 := e.indent
|
||||
e.indent++
|
||||
|
||||
// Join all of the element texts together, separated with newlines
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
elem, elemCm := e.unpackNode(value.Index(i), Comments{})
|
||||
|
||||
if elemCm.Before == "" && elemCm.Key == "" {
|
||||
e.writeIndent(e.indent)
|
||||
} else {
|
||||
e.WriteString(e.Eol + elemCm.Before + elemCm.Key)
|
||||
}
|
||||
|
||||
if err := e.str(elem, true, "", false, false, elemCm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.WriteString(elemCm.After)
|
||||
}
|
||||
|
||||
if cm.InsideLast != "" {
|
||||
e.WriteString(e.Eol + cm.InsideLast)
|
||||
} else {
|
||||
e.writeIndent(indent1)
|
||||
}
|
||||
e.WriteString("]")
|
||||
|
||||
e.indent = indent1
|
||||
|
||||
case reflect.Map:
|
||||
var fis []fieldInfo
|
||||
useMarshalText := value.Type().Key().Implements(marshalerText)
|
||||
keys := value.MapKeys()
|
||||
sort.Sort(sortAlpha(keys))
|
||||
for _, key := range keys {
|
||||
var name string
|
||||
if useMarshalText {
|
||||
keyBytes, err := key.Interface().(encoding.TextMarshaler).MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name = string(keyBytes)
|
||||
} else {
|
||||
name = fmt.Sprintf("%v", key)
|
||||
}
|
||||
fis = append(fis, fieldInfo{
|
||||
field: value.MapIndex(key),
|
||||
name: name,
|
||||
})
|
||||
}
|
||||
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
|
||||
|
||||
case reflect.Struct:
|
||||
// Struct field info is identical for all instances of the same type.
|
||||
// Only the values on the fields can be different.
|
||||
t := value.Type()
|
||||
sfis, ok := e.structTypeCache[t]
|
||||
if !ok {
|
||||
sfis = getStructFieldInfoSlice(t)
|
||||
e.structTypeCache[t] = sfis
|
||||
}
|
||||
|
||||
// Collect fields first, too see if any should be shown (considering
|
||||
// "omitEmpty").
|
||||
var fis []fieldInfo
|
||||
FieldLoop:
|
||||
for _, sfi := range sfis {
|
||||
// The field might be found on the root struct or in embedded structs.
|
||||
fv := value
|
||||
for _, i := range sfi.indexPath {
|
||||
if fv.Kind() == reflect.Ptr {
|
||||
if fv.IsNil() {
|
||||
continue FieldLoop
|
||||
}
|
||||
fv = fv.Elem()
|
||||
}
|
||||
fv = fv.Field(i)
|
||||
}
|
||||
|
||||
if sfi.omitEmpty && isEmptyValue(fv) {
|
||||
continue
|
||||
}
|
||||
|
||||
fi := fieldInfo{
|
||||
field: fv,
|
||||
name: sfi.name,
|
||||
}
|
||||
if e.Comments {
|
||||
fi.comment = sfi.comment
|
||||
}
|
||||
fis = append(fis, fi)
|
||||
}
|
||||
return e.writeFields(fis, noIndent, separator, isRootObject, isObjElement, cm)
|
||||
|
||||
default:
|
||||
return errors.New("Unsupported type " + value.Type().String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEmptyValue(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||
return v.Len() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func investigateComment(txt string) (
|
||||
endsInsideComment,
|
||||
endsWithLineFeed bool,
|
||||
) {
|
||||
var prev rune
|
||||
for _, rn := range txt {
|
||||
switch rn {
|
||||
case '\n':
|
||||
endsInsideComment = false
|
||||
case '#':
|
||||
endsInsideComment = true
|
||||
case '/':
|
||||
if prev == '/' {
|
||||
endsInsideComment = true
|
||||
}
|
||||
}
|
||||
endsWithLineFeed = (rn == '\n')
|
||||
prev = rn
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal returns the Hjson encoding of v using
|
||||
// default options.
|
||||
//
|
||||
// See MarshalWithOptions.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return MarshalWithOptions(v, DefaultOptions())
|
||||
}
|
||||
|
||||
// MarshalWithOptions returns the Hjson encoding of v.
|
||||
//
|
||||
// The value v is traversed recursively.
|
||||
//
|
||||
// Boolean values are written as true or false.
|
||||
//
|
||||
// Floating point, integer, and json.Number values are written as numbers (with
|
||||
// decimals only if needed, using . as decimals separator).
|
||||
//
|
||||
// String values encode as Hjson strings (quoteless, multiline or
|
||||
// JSON).
|
||||
//
|
||||
// Array and slice values encode as arrays, surrounded by []. Unlike
|
||||
// json.Marshal, hjson.Marshal will encode a nil-slice as [] instead of null.
|
||||
//
|
||||
// Map values encode as objects, surrounded by {}. The map's key type must be
|
||||
// possible to print to a string using fmt.Sprintf("%v", key), or implement
|
||||
// encoding.TextMarshaler. The map keys are sorted alphabetically and
|
||||
// used as object keys. Unlike json.Marshal, hjson.Marshal will encode a
|
||||
// nil-map as {} instead of null.
|
||||
//
|
||||
// Struct values also encode as objects, surrounded by {}. Only the exported
|
||||
// fields are encoded to Hjson. The fields will appear in the same order as in
|
||||
// the struct.
|
||||
//
|
||||
// The encoding of each struct field can be customized by the format string
|
||||
// stored under the "json" key in the struct field's tag.
|
||||
// The format string gives the name of the field, possibly followed by a comma
|
||||
// and "omitempty". The name may be empty in order to specify "omitempty"
|
||||
// without overriding the default field name.
|
||||
//
|
||||
// The "omitempty" option specifies that the field should be omitted
|
||||
// from the encoding if the field has an empty value, defined as
|
||||
// false, 0, a nil pointer, a nil interface value, and any empty array,
|
||||
// slice, map, or string.
|
||||
//
|
||||
// As a special case, if the field tag is "-", the field is always omitted.
|
||||
// Note that a field with name "-" can still be generated using the tag "-,".
|
||||
//
|
||||
// Comments can be set on struct fields using the "comment" key in the struct
|
||||
// field's tag. The comment will be written on the line before the field key,
|
||||
// prefixed with #. Or possible several lines prefixed by #, if there are line
|
||||
// breaks (\n) in the comment text.
|
||||
//
|
||||
// If both the "json" and the "comment" tag keys are used on a struct field
|
||||
// they should be separated by whitespace.
|
||||
//
|
||||
// Examples of struct field tags and their meanings:
|
||||
//
|
||||
// // Field appears in Hjson as key "myName".
|
||||
// Field int `json:"myName"`
|
||||
//
|
||||
// // Field appears in Hjson as key "myName" and the field is omitted from
|
||||
// // the object if its value is empty, as defined above.
|
||||
// Field int `json:"myName,omitempty"`
|
||||
//
|
||||
// // Field appears in Hjson as key "Field" (the default), but the field is
|
||||
// // skipped if empty. Note the leading comma.
|
||||
// Field int `json:",omitempty"`
|
||||
//
|
||||
// // Field is ignored by this package.
|
||||
// Field int `json:"-"`
|
||||
//
|
||||
// // Field appears in Hjson as key "-".
|
||||
// Field int `json:"-,"`
|
||||
//
|
||||
// // Field appears in Hjson preceded by a line just containing `# A comment.`
|
||||
// Field int `comment:"A comment."`
|
||||
//
|
||||
// // Field appears in Hjson as key "myName" preceded by a line just
|
||||
// // containing `# A comment.`
|
||||
// Field int `json:"myName" comment:"A comment."`
|
||||
//
|
||||
// Anonymous struct fields are usually marshaled as if their inner exported fields
|
||||
// were fields in the outer struct, subject to the usual Go visibility rules amended
|
||||
// as described in the next paragraph.
|
||||
// An anonymous struct field with a name given in its JSON tag is treated as
|
||||
// having that name, rather than being anonymous.
|
||||
// An anonymous struct field of interface type is treated the same as having
|
||||
// that type as its name, rather than being anonymous.
|
||||
//
|
||||
// The Go visibility rules for struct fields are amended for JSON when
|
||||
// deciding which field to marshal or unmarshal. If there are
|
||||
// multiple fields at the same level, and that level is the least
|
||||
// nested (and would therefore be the nesting level selected by the
|
||||
// usual Go rules), the following extra rules apply:
|
||||
//
|
||||
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
|
||||
// even if there are multiple untagged fields that would otherwise conflict.
|
||||
//
|
||||
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
|
||||
//
|
||||
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
|
||||
//
|
||||
// Pointer values encode as the value pointed to.
|
||||
// A nil pointer encodes as the null JSON value.
|
||||
//
|
||||
// Interface values encode as the value contained in the interface.
|
||||
// A nil interface value encodes as the null JSON value.
|
||||
//
|
||||
// If an encountered value implements the json.Marshaler interface then the
|
||||
// function MarshalJSON() is called on it. The JSON is then converted to Hjson
|
||||
// using the current indentation and options given in the call to json.Marshal().
|
||||
//
|
||||
// If an encountered value implements the encoding.TextMarshaler interface
|
||||
// but not the json.Marshaler interface, then the function MarshalText() is
|
||||
// called on it to get a text.
|
||||
//
|
||||
// Channel, complex, and function values cannot be encoded in Hjson, will
|
||||
// result in an error.
|
||||
//
|
||||
// Hjson cannot represent cyclic data structures and Marshal does not handle
|
||||
// them. Passing cyclic structures to Marshal will result in an error.
|
||||
func MarshalWithOptions(v interface{}, options EncoderOptions) ([]byte, error) {
|
||||
e := &hjsonEncoder{
|
||||
indent: 0,
|
||||
EncoderOptions: options,
|
||||
structTypeCache: map[reflect.Type][]structFieldInfo{},
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(v)
|
||||
_, cm := e.unpackNode(value, Comments{})
|
||||
e.WriteString(cm.Before + cm.Key)
|
||||
|
||||
err := e.str(value, true, e.BaseIndentation, true, false, cm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.WriteString(cm.After)
|
||||
|
||||
return e.Bytes(), nil
|
||||
}
|
||||
415
vendor/github.com/hjson/hjson-go/v4/node.go
generated
vendored
Normal file
415
vendor/github.com/hjson/hjson-go/v4/node.go
generated
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
package hjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Comments struct {
|
||||
// Comment/whitespace on line(s) before the value, and before the value on
|
||||
// the same line. If not empty, is expected to end with a line feed +
|
||||
// indentation for the value.
|
||||
Before string
|
||||
// Comment/whitespace between the key and this value, if this value is an
|
||||
// element in a map/object.
|
||||
Key string
|
||||
// Comment/whitespace after (but still on the same line as) the leading
|
||||
// bracket ({ or [) for this value, if this value is a slice/array or
|
||||
// map/object. Is not expected to contain any line feed.
|
||||
InsideFirst string
|
||||
// Comment/whitespace from the beginning of the first line after all child
|
||||
// values belonging to this value, until the closing bracket (} or ]), if
|
||||
// this value is a slice/array or map/object. If not empty, is expected to
|
||||
// end with a line feed + indentation for the closing bracket.
|
||||
InsideLast string
|
||||
// Comment/whitespace after (but still on the same line as) the value. Is not
|
||||
// expected to contain any line feed. hjson.Unmarshal() will try to assign
|
||||
// comments/whitespace from lines between values to `Before` on the value
|
||||
// after those lines, or to `InsideLast` on the slice/map if the lines
|
||||
// containing comments appear after the last element inside a slice/map.
|
||||
After string
|
||||
}
|
||||
|
||||
// Node must be used as destination for Unmarshal() or UnmarshalWithOptions()
|
||||
// whenever comments should be read from the input. The struct is simply a
|
||||
// wrapper for the actual values and a helper struct containing any comments.
|
||||
// The Value in the destination Node will be overwritten in the call to
|
||||
// Unmarshal() or UnmarshalWithOptions(), i.e. node trees are not merged.
|
||||
// After the unmarshal, Node.Value will contain any of these types:
|
||||
//
|
||||
// nil (no type)
|
||||
// float64 (if UseJSONNumber == false)
|
||||
// json.Number (if UseJSONNumber == true)
|
||||
// string
|
||||
// bool
|
||||
// []interface{}
|
||||
// *hjson.OrderedMap
|
||||
//
|
||||
// All elements in an []interface{} or *hjson.OrderedMap will be of the type
|
||||
// *hjson.Node, so that they can contain comments.
|
||||
//
|
||||
// This example shows unmarshalling input with comments, changing the value on
|
||||
// a single key (the input is assumed to have an object/map as root) and then
|
||||
// marshalling the node tree again, including comments and with preserved key
|
||||
// order in the object/map.
|
||||
//
|
||||
// var node hjson.Node
|
||||
// err := hjson.Unmarshal(input, &node)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// _, err = node.SetKey("setting1", 3)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// output, err := hjson.Marshal(node)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
type Node struct {
|
||||
Value interface{}
|
||||
Cm Comments
|
||||
}
|
||||
|
||||
// Len returns the length of the value wrapped by this Node, if the value is of
|
||||
// type *hjson.OrderedMap, []interface{} or string. Otherwise 0 is returned.
|
||||
func (c *Node) Len() int {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
return cont.Len()
|
||||
case []interface{}:
|
||||
return len(cont)
|
||||
case string:
|
||||
return len(cont)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// AtIndex returns the key (if any) and value (unwrapped from its Node) found
|
||||
// at the specified index, if this Node contains a value of type
|
||||
// *hjson.OrderedMap or []interface{}. Returns an error for unexpected types.
|
||||
// Panics if index < 0 or index >= Len().
|
||||
func (c *Node) AtIndex(index int) (string, interface{}, error) {
|
||||
if c == nil {
|
||||
return "", nil, fmt.Errorf("Node is nil")
|
||||
}
|
||||
var key string
|
||||
var elem interface{}
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
key = cont.Keys[index]
|
||||
elem = cont.Map[key]
|
||||
case []interface{}:
|
||||
elem = cont[index]
|
||||
default:
|
||||
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
node, ok := elem.(*Node)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
|
||||
}
|
||||
return key, node.Value, nil
|
||||
}
|
||||
|
||||
// AtKey returns the value (unwrapped from its Node) found for the specified
|
||||
// key, if this Node contains a value of type *hjson.OrderedMap. An error is
|
||||
// returned for unexpected types. The second returned value is true if the key
|
||||
// was found, false otherwise.
|
||||
func (c *Node) AtKey(key string) (interface{}, bool, error) {
|
||||
if c == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
om, ok := c.Value.(*OrderedMap)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
elem, ok := om.Map[key]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
node, ok := elem.(*Node)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("Unexpected element type: %v", reflect.TypeOf(elem))
|
||||
}
|
||||
return node.Value, true, nil
|
||||
}
|
||||
|
||||
// Append adds the input value to the end of the []interface{} wrapped by this
|
||||
// Node. If this Node contains nil without a type, an empty []interface{} is
|
||||
// first created. If this Node contains a value of any other type, an error is
|
||||
// returned.
|
||||
func (c *Node) Append(value interface{}) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("Node is nil")
|
||||
}
|
||||
var arr []interface{}
|
||||
if c.Value == nil {
|
||||
arr = []interface{}{}
|
||||
} else {
|
||||
var ok bool
|
||||
arr, ok = c.Value.([]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
}
|
||||
c.Value = append(arr, &Node{Value: value})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Insert inserts a new key/value pair at the specified index, if this Node
|
||||
// contains a value of type *hjson.OrderedMap or []interface{}. Returns an error
|
||||
// for unexpected types. Panics if index < 0 or index > c.Len(). If the key
|
||||
// already exists in the OrderedMap, the new value is set but the position of
|
||||
// the key is not changed. Otherwise the value to insert is wrapped in a new
|
||||
// Node. If this Node contains []interface{}, the key is ignored. Returns the
|
||||
// old value and true if the key already exists in the/ OrderedMap, nil and
|
||||
// false otherwise.
|
||||
func (c *Node) Insert(index int, key string, value interface{}) (interface{}, bool, error) {
|
||||
if c == nil {
|
||||
return nil, false, fmt.Errorf("Node is nil")
|
||||
}
|
||||
var oldVal interface{}
|
||||
var found bool
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
oldVal, found := cont.Map[key]
|
||||
if found {
|
||||
if node, ok := oldVal.(*Node); ok {
|
||||
oldVal = node.Value
|
||||
node.Value = value
|
||||
} else {
|
||||
cont.Map[key] = &Node{Value: value}
|
||||
}
|
||||
} else {
|
||||
oldVal, found = cont.Insert(index, key, &Node{Value: value})
|
||||
}
|
||||
case []interface{}:
|
||||
value = &Node{Value: value}
|
||||
if index == len(cont) {
|
||||
c.Value = append(cont, value)
|
||||
} else {
|
||||
cont = append(cont[:index+1], cont[index:]...)
|
||||
cont[index] = value
|
||||
c.Value = cont
|
||||
}
|
||||
default:
|
||||
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
return oldVal, found, nil
|
||||
}
|
||||
|
||||
// SetIndex assigns the specified value to the child Node found at the specified
|
||||
// index, if this Node contains a value of type *hjson.OrderedMap or
|
||||
// []interface{}. Returns an error for unexpected types. Returns the key (if
|
||||
// any) and value previously found at the specified index. Panics if index < 0
|
||||
// or index >= Len().
|
||||
func (c *Node) SetIndex(index int, value interface{}) (string, interface{}, error) {
|
||||
if c == nil {
|
||||
return "", nil, fmt.Errorf("Node is nil")
|
||||
}
|
||||
var key string
|
||||
var elem interface{}
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
key = cont.Keys[index]
|
||||
elem = cont.Map[key]
|
||||
case []interface{}:
|
||||
elem = cont[index]
|
||||
default:
|
||||
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
var oldVal interface{}
|
||||
node, ok := elem.(*Node)
|
||||
if ok {
|
||||
oldVal = node.Value
|
||||
node.Value = value
|
||||
} else {
|
||||
oldVal = elem
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
cont.Map[key] = &Node{Value: value}
|
||||
case []interface{}:
|
||||
cont[index] = &Node{Value: value}
|
||||
}
|
||||
}
|
||||
return key, oldVal, nil
|
||||
}
|
||||
|
||||
// SetKey assigns the specified value to the child Node identified by the
|
||||
// specified key, if this Node contains a value of the type *hjson.OrderedMap.
|
||||
// If this Node contains nil without a type, an empty *hjson.OrderedMap is
|
||||
// first created. If this Node contains a value of any other type an error is
|
||||
// returned. If the key cannot be found in the OrderedMap, a new Node is
|
||||
// created, wrapping the specified value, and appended to the end of the
|
||||
// OrderedMap. Returns the old value and true if the key already existed in
|
||||
// the OrderedMap, nil and false otherwise.
|
||||
func (c *Node) SetKey(key string, value interface{}) (interface{}, bool, error) {
|
||||
if c == nil {
|
||||
return nil, false, fmt.Errorf("Node is nil")
|
||||
}
|
||||
var om *OrderedMap
|
||||
if c.Value == nil {
|
||||
om = NewOrderedMap()
|
||||
c.Value = om
|
||||
} else {
|
||||
var ok bool
|
||||
om, ok = c.Value.(*OrderedMap)
|
||||
if !ok {
|
||||
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
}
|
||||
var oldVal interface{}
|
||||
elem, ok := om.Map[key]
|
||||
if ok {
|
||||
var node *Node
|
||||
node, ok = elem.(*Node)
|
||||
if ok {
|
||||
oldVal = node.Value
|
||||
node.Value = value
|
||||
}
|
||||
}
|
||||
foundKey := true
|
||||
if !ok {
|
||||
oldVal, foundKey = om.Set(key, &Node{Value: value})
|
||||
}
|
||||
return oldVal, foundKey, nil
|
||||
}
|
||||
|
||||
// DeleteIndex deletes the value or key/value pair found at the specified index,
|
||||
// if this Node contains a value of type *hjson.OrderedMap or []interface{}.
|
||||
// Returns an error for unexpected types. Panics if index < 0 or
|
||||
// index >= c.Len(). Returns the deleted key (if any) and value.
|
||||
func (c *Node) DeleteIndex(index int) (string, interface{}, error) {
|
||||
if c == nil {
|
||||
return "", nil, fmt.Errorf("Node is nil")
|
||||
}
|
||||
var key string
|
||||
var value interface{}
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
key, value = cont.DeleteIndex(index)
|
||||
case []interface{}:
|
||||
value = cont[index]
|
||||
cont = append(cont[:index], cont[index+1:]...)
|
||||
c.Value = cont
|
||||
default:
|
||||
return "", nil, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
if node, ok := value.(*Node); ok {
|
||||
value = node.Value
|
||||
}
|
||||
return key, value, nil
|
||||
}
|
||||
|
||||
// DeleteKey deletes the key/value pair with the specified key, if found and if
|
||||
// this Node contains a value of type *hjson.OrderedMap. Returns an error for
|
||||
// unexpected types. Returns the deleted value and true if the key was found,
|
||||
// nil and false otherwise.
|
||||
func (c *Node) DeleteKey(key string) (interface{}, bool, error) {
|
||||
if c == nil {
|
||||
return nil, false, fmt.Errorf("Node is nil")
|
||||
}
|
||||
if om, ok := c.Value.(*OrderedMap); ok {
|
||||
oldValue, found := om.DeleteKey(key)
|
||||
if node, ok := oldValue.(*Node); ok {
|
||||
oldValue = node.Value
|
||||
}
|
||||
return oldValue, found, nil
|
||||
}
|
||||
return nil, false, fmt.Errorf("Unexpected value type: %v", reflect.TypeOf(c.Value))
|
||||
}
|
||||
|
||||
// NI is an acronym formed from "get Node pointer by Index". Returns the *Node
|
||||
// element found at the specified index, if this Node contains a value of type
|
||||
// *hjson.OrderedMap or []interface{}. Returns nil otherwise. Panics if
|
||||
// index < 0 or index >= Len(). Does not create or alter any value.
|
||||
func (c *Node) NI(index int) *Node {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
var elem interface{}
|
||||
switch cont := c.Value.(type) {
|
||||
case *OrderedMap:
|
||||
elem = cont.AtIndex(index)
|
||||
case []interface{}:
|
||||
elem = cont[index]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if node, ok := elem.(*Node); ok {
|
||||
return node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NK is an acronym formed from "get Node pointer by Key". Returns the *Node
|
||||
// element found for the specified key, if this Node contains a value of type
|
||||
// *hjson.OrderedMap. Returns nil otherwise. Does not create or alter anything.
|
||||
func (c *Node) NK(key string) *Node {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
om, ok := c.Value.(*OrderedMap)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if elem, ok := om.Map[key]; ok {
|
||||
if node, ok := elem.(*Node); ok {
|
||||
return node
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NKC is an acronym formed from "get Node pointer by Key, Create if not found".
|
||||
// Returns the *Node element found for the specified key, if this Node contains
|
||||
// a value of type *hjson.OrderedMap. If this Node contains nil without a type,
|
||||
// an empty *hjson.OrderedMap is first created. If this Node contains a value of
|
||||
// any other type or if the element idendified by the specified key is not of
|
||||
// type *Node, an error is returned. If the key cannot be found in the
|
||||
// OrderedMap, a new Node is created for the specified key. Example usage:
|
||||
//
|
||||
// var node hjson.Node
|
||||
// node.NKC("rootKey1").NKC("subKey1").SetKey("valKey1", "my value")
|
||||
func (c *Node) NKC(key string) *Node {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
var om *OrderedMap
|
||||
if c.Value == nil {
|
||||
om = NewOrderedMap()
|
||||
c.Value = om
|
||||
} else {
|
||||
var ok bool
|
||||
om, ok = c.Value.(*OrderedMap)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if elem, ok := om.Map[key]; ok {
|
||||
if node, ok := elem.(*Node); ok {
|
||||
return node
|
||||
}
|
||||
} else {
|
||||
node := &Node{}
|
||||
om.Set(key, node)
|
||||
return node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
|
||||
// hjson.Node trees to be used as input for json.Marshal().
|
||||
func (c Node) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(c.Value)
|
||||
}
|
||||
|
||||
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
|
||||
// enabling hjson.Node to be used as destination for json.Unmarshal().
|
||||
func (c *Node) UnmarshalJSON(b []byte) error {
|
||||
return Unmarshal(b, c)
|
||||
}
|
||||
167
vendor/github.com/hjson/hjson-go/v4/orderedmap.go
generated
vendored
Normal file
167
vendor/github.com/hjson/hjson-go/v4/orderedmap.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
package hjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// OrderedMap wraps a map and a slice containing all of the keys from the map,
|
||||
// so that the order of the keys can be specified. The Keys slice can be sorted
|
||||
// or rearranged like any other slice, but do not add or remove keys manually
|
||||
// on it. Use OrderedMap.Insert(), OrderedMap.Set(), OrderedMap.DeleteIndex() or
|
||||
// OrderedMap.DeleteKey() instead.
|
||||
//
|
||||
// Example of how to iterate through the elements of an OrderedMap in order:
|
||||
//
|
||||
// for _, key := range om.Keys {
|
||||
// fmt.Printf("%v\n", om.Map[key])
|
||||
// }
|
||||
//
|
||||
// Always use the functions Insert() or Set() instead of setting values
|
||||
// directly on OrderedMap.Map, because any new keys must also be added to
|
||||
// OrderedMap.Keys. Otherwise those keys will be ignored when iterating through
|
||||
// the elements of the OrderedMap in order, as for example happens in the
|
||||
// function hjson.Marshal().
|
||||
type OrderedMap struct {
|
||||
Keys []string
|
||||
Map map[string]interface{}
|
||||
}
|
||||
|
||||
// KeyValue is only used as input to NewOrderedMapFromSlice().
|
||||
type KeyValue struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewOrderedMap returns a pointer to a new OrderedMap. An OrderedMap should
|
||||
// always be passed by reference, never by value. If an OrderedMap is passed
|
||||
// by value then appending new keys won't affect all of the copies of the
|
||||
// OrderedMap.
|
||||
func NewOrderedMap() *OrderedMap {
|
||||
return &OrderedMap{
|
||||
Keys: nil,
|
||||
Map: map[string]interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
// NewOrderedMapFromSlice is like NewOrderedMap but with initial values.
|
||||
// Example:
|
||||
//
|
||||
// om := NewOrderedMapFromSlice([]KeyValue{
|
||||
// {"B", "first"},
|
||||
// {"A", "second"},
|
||||
// })
|
||||
func NewOrderedMapFromSlice(args []KeyValue) *OrderedMap {
|
||||
c := NewOrderedMap()
|
||||
for _, elem := range args {
|
||||
c.Set(elem.Key, elem.Value)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Len returns the number of values contained in the OrderedMap.
|
||||
func (c *OrderedMap) Len() int {
|
||||
return len(c.Keys)
|
||||
}
|
||||
|
||||
// AtIndex returns the value found at the specified index. Panics if
|
||||
// index < 0 or index >= c.Len().
|
||||
func (c *OrderedMap) AtIndex(index int) interface{} {
|
||||
return c.Map[c.Keys[index]]
|
||||
}
|
||||
|
||||
// AtKey returns the value found for the specified key, and true if the value
|
||||
// was found. Returns nil and false if the value was not found.
|
||||
func (c *OrderedMap) AtKey(key string) (interface{}, bool) {
|
||||
ret, ok := c.Map[key]
|
||||
return ret, ok
|
||||
}
|
||||
|
||||
// Insert inserts a new key/value pair at the specified index. Panics if
|
||||
// index < 0 or index > c.Len(). If the key already exists in the OrderedMap,
|
||||
// the new value is set but the position of the key is not changed. Returns
|
||||
// the old value and true if the key already exists in the OrderedMap, nil and
|
||||
// false otherwise.
|
||||
func (c *OrderedMap) Insert(index int, key string, value interface{}) (interface{}, bool) {
|
||||
oldValue, exists := c.Map[key]
|
||||
c.Map[key] = value
|
||||
if exists {
|
||||
return oldValue, true
|
||||
}
|
||||
if index == len(c.Keys) {
|
||||
c.Keys = append(c.Keys, key)
|
||||
} else {
|
||||
c.Keys = append(c.Keys[:index+1], c.Keys[index:]...)
|
||||
c.Keys[index] = key
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Set sets the specified value for the specified key. If the key does not
|
||||
// already exist in the OrderedMap it is appended to the end of the OrderedMap.
|
||||
// If the key already exists in the OrderedMap, the new value is set but the
|
||||
// position of the key is not changed. Returns the old value and true if the
|
||||
// key already exists in the OrderedMap, nil and false otherwise.
|
||||
func (c *OrderedMap) Set(key string, value interface{}) (interface{}, bool) {
|
||||
return c.Insert(len(c.Keys), key, value)
|
||||
}
|
||||
|
||||
// DeleteIndex deletes the key/value pair found at the specified index.
|
||||
// Returns the deleted key and value. Panics if index < 0 or index >= c.Len().
|
||||
func (c *OrderedMap) DeleteIndex(index int) (string, interface{}) {
|
||||
key := c.Keys[index]
|
||||
value := c.Map[key]
|
||||
delete(c.Map, key)
|
||||
c.Keys = append(c.Keys[:index], c.Keys[index+1:]...)
|
||||
return key, value
|
||||
}
|
||||
|
||||
// DeleteKey deletes the key/value pair with the specified key, if found.
|
||||
// Returns the deleted value and true if the key was found, nil and false
|
||||
// otherwise.
|
||||
func (c *OrderedMap) DeleteKey(key string) (interface{}, bool) {
|
||||
for index, ck := range c.Keys {
|
||||
if ck == key {
|
||||
_, value := c.DeleteIndex(index)
|
||||
return value, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// MarshalJSON is an implementation of the json.Marshaler interface, enabling
|
||||
// hjson.OrderedMap to be used as input for json.Marshal().
|
||||
func (c *OrderedMap) MarshalJSON() ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
|
||||
b.WriteString("{")
|
||||
|
||||
for index, key := range c.Keys {
|
||||
if index > 0 {
|
||||
b.WriteString(",")
|
||||
}
|
||||
jbuf, err := json.Marshal(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(jbuf)
|
||||
b.WriteString(":")
|
||||
jbuf, err = json.Marshal(c.Map[key])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(jbuf)
|
||||
}
|
||||
|
||||
b.WriteString("}")
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON is an implementation of the json.Unmarshaler interface,
|
||||
// enabling hjson.OrderedMap to be used as destination for json.Unmarshal().
|
||||
func (c *OrderedMap) UnmarshalJSON(b []byte) error {
|
||||
c.Keys = nil
|
||||
c.Map = map[string]interface{}{}
|
||||
return Unmarshal(b, c)
|
||||
}
|
||||
116
vendor/github.com/hjson/hjson-go/v4/parseNumber.go
generated
vendored
Normal file
116
vendor/github.com/hjson/hjson-go/v4/parseNumber.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package hjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type parseNumber struct {
|
||||
data []byte
|
||||
at int // The index of the current character
|
||||
ch byte // The current character
|
||||
}
|
||||
|
||||
func (p *parseNumber) next() bool {
|
||||
// get the next character.
|
||||
len := len(p.data)
|
||||
if p.at < len {
|
||||
p.ch = p.data[p.at]
|
||||
p.at++
|
||||
return true
|
||||
}
|
||||
if p.at == len {
|
||||
p.at++
|
||||
p.ch = 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *parseNumber) peek(offs int) byte {
|
||||
pos := p.at + offs
|
||||
if pos >= 0 && pos < len(p.data) {
|
||||
return p.data[pos]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func startsWithNumber(text []byte) bool {
|
||||
if _, err := tryParseNumber(text, true, false); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func tryParseNumber(text []byte, stopAtNext, useJSONNumber bool) (interface{}, error) {
|
||||
// Parse a number value.
|
||||
|
||||
p := parseNumber{
|
||||
data: text,
|
||||
at: 0,
|
||||
ch: ' ',
|
||||
}
|
||||
leadingZeros := 0
|
||||
testLeading := true
|
||||
p.next()
|
||||
if p.ch == '-' {
|
||||
p.next()
|
||||
}
|
||||
for p.ch >= '0' && p.ch <= '9' {
|
||||
if testLeading {
|
||||
if p.ch == '0' {
|
||||
leadingZeros++
|
||||
} else {
|
||||
testLeading = false
|
||||
}
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
if testLeading {
|
||||
leadingZeros--
|
||||
} // single 0 is allowed
|
||||
if p.ch == '.' {
|
||||
for p.next() && p.ch >= '0' && p.ch <= '9' {
|
||||
}
|
||||
}
|
||||
if p.ch == 'e' || p.ch == 'E' {
|
||||
p.next()
|
||||
if p.ch == '-' || p.ch == '+' {
|
||||
p.next()
|
||||
}
|
||||
for p.ch >= '0' && p.ch <= '9' {
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
|
||||
end := p.at
|
||||
|
||||
// skip white/to (newline)
|
||||
for p.ch > 0 && p.ch <= ' ' {
|
||||
p.next()
|
||||
}
|
||||
|
||||
if stopAtNext {
|
||||
// end scan if we find a punctuator character like ,}] or a comment
|
||||
if p.ch == ',' || p.ch == '}' || p.ch == ']' ||
|
||||
p.ch == '#' || p.ch == '/' && (p.peek(0) == '/' || p.peek(0) == '*') {
|
||||
p.ch = 0
|
||||
}
|
||||
}
|
||||
|
||||
if p.ch > 0 || leadingZeros != 0 {
|
||||
return 0, errors.New("Invalid number")
|
||||
}
|
||||
if useJSONNumber {
|
||||
return json.Number(string(p.data[0 : end-1])), nil
|
||||
}
|
||||
number, err := strconv.ParseFloat(string(p.data[0:end-1]), 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if math.IsInf(number, 0) || math.IsNaN(number) {
|
||||
return 0, errors.New("Invalid number")
|
||||
}
|
||||
return number, nil
|
||||
}
|
||||
346
vendor/github.com/hjson/hjson-go/v4/structs.go
generated
vendored
Normal file
346
vendor/github.com/hjson/hjson-go/v4/structs.go
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
package hjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type fieldInfo struct {
|
||||
field reflect.Value
|
||||
name string
|
||||
comment string
|
||||
}
|
||||
|
||||
type structFieldInfo struct {
|
||||
name string
|
||||
tagged bool
|
||||
comment string
|
||||
omitEmpty bool
|
||||
indexPath []int
|
||||
}
|
||||
|
||||
// Use lower key name as key. Values are arrays in case some fields only differ
|
||||
// by upper/lower case.
|
||||
type structFieldMap map[string][]structFieldInfo
|
||||
|
||||
func (s structFieldMap) insert(sfi structFieldInfo) {
|
||||
key := strings.ToLower(sfi.name)
|
||||
s[key] = append(s[key], sfi)
|
||||
}
|
||||
|
||||
func (s structFieldMap) getField(name string) (structFieldInfo, bool) {
|
||||
key := strings.ToLower(name)
|
||||
if arr, ok := s[key]; ok {
|
||||
for _, elem := range arr {
|
||||
if elem.name == name {
|
||||
return elem, true
|
||||
}
|
||||
}
|
||||
return arr[0], true
|
||||
}
|
||||
|
||||
return structFieldInfo{}, false
|
||||
}
|
||||
|
||||
// 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 []structFieldInfo) (structFieldInfo, bool) {
|
||||
// The fields are sorted in increasing index-length order, then by presence of tag.
|
||||
// That means that the first field is the dominant one. We need only check
|
||||
// for error cases: two fields at top level, either both tagged or neither tagged.
|
||||
if len(fields) > 1 && len(fields[0].indexPath) == len(fields[1].indexPath) && fields[0].tagged == fields[1].tagged {
|
||||
return structFieldInfo{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
// byIndex sorts by index sequence.
|
||||
type byIndex []structFieldInfo
|
||||
|
||||
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].indexPath {
|
||||
if k >= len(x[j].indexPath) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].indexPath[k] {
|
||||
return xik < x[j].indexPath[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].indexPath) < len(x[j].indexPath)
|
||||
}
|
||||
|
||||
func getStructFieldInfo(rootType reflect.Type) []structFieldInfo {
|
||||
type structInfo struct {
|
||||
typ reflect.Type
|
||||
indexPath []int
|
||||
}
|
||||
var sfis []structFieldInfo
|
||||
structsToInvestigate := []structInfo{structInfo{typ: rootType}}
|
||||
// Struct types already visited at an earlier depth.
|
||||
visited := map[reflect.Type]bool{}
|
||||
// Count the number of specific struct types on a specific depth.
|
||||
typeDepthCount := map[reflect.Type]int{}
|
||||
|
||||
for len(structsToInvestigate) > 0 {
|
||||
curStructs := structsToInvestigate
|
||||
structsToInvestigate = []structInfo{}
|
||||
curTDC := typeDepthCount
|
||||
typeDepthCount = map[reflect.Type]int{}
|
||||
|
||||
for _, curStruct := range curStructs {
|
||||
if visited[curStruct.typ] {
|
||||
// The struct type has already appeared on an earlier depth. Fields on
|
||||
// an earlier depth always have precedence over fields with identical
|
||||
// name on a later depth, so no point in investigating this type again.
|
||||
continue
|
||||
}
|
||||
visited[curStruct.typ] = true
|
||||
|
||||
for i := 0; i < curStruct.typ.NumField(); i++ {
|
||||
sf := curStruct.typ.Field(i)
|
||||
|
||||
if sf.Anonymous {
|
||||
t := sf.Type
|
||||
if t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
// If the field is not exported and not a struct.
|
||||
if sf.PkgPath != "" && t.Kind() != reflect.Struct {
|
||||
// Ignore embedded fields of unexported non-struct types.
|
||||
continue
|
||||
}
|
||||
// Do not ignore embedded fields of unexported struct types
|
||||
// since they may have exported fields.
|
||||
} else if sf.PkgPath != "" {
|
||||
// Ignore unexported non-embedded fields.
|
||||
continue
|
||||
}
|
||||
|
||||
jsonTag := sf.Tag.Get("json")
|
||||
if jsonTag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
sfi := structFieldInfo{
|
||||
name: sf.Name,
|
||||
comment: sf.Tag.Get("comment"),
|
||||
}
|
||||
|
||||
splits := strings.Split(jsonTag, ",")
|
||||
if splits[0] != "" {
|
||||
sfi.name = splits[0]
|
||||
sfi.tagged = true
|
||||
}
|
||||
if len(splits) > 1 {
|
||||
for _, opt := range splits[1:] {
|
||||
if opt == "omitempty" {
|
||||
sfi.omitEmpty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sfi.indexPath = make([]int, len(curStruct.indexPath)+1)
|
||||
copy(sfi.indexPath, curStruct.indexPath)
|
||||
sfi.indexPath[len(curStruct.indexPath)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// If the current field should be included.
|
||||
if sfi.tagged || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
sfis = append(sfis, sfi)
|
||||
if curTDC[curStruct.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.
|
||||
sfis = append(sfis, sfi)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
typeDepthCount[ft]++
|
||||
if typeDepthCount[ft] == 1 {
|
||||
structsToInvestigate = append(structsToInvestigate, structInfo{
|
||||
typ: ft,
|
||||
indexPath: sfi.indexPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(sfis, func(i, j int) bool {
|
||||
// sort field by name, breaking ties with depth, then
|
||||
// breaking ties with "name came from json tag", then
|
||||
// breaking ties with index sequence.
|
||||
if sfis[i].name != sfis[j].name {
|
||||
return sfis[i].name < sfis[j].name
|
||||
}
|
||||
if len(sfis[i].indexPath) != len(sfis[j].indexPath) {
|
||||
return len(sfis[i].indexPath) < len(sfis[j].indexPath)
|
||||
}
|
||||
if sfis[i].tagged != sfis[j].tagged {
|
||||
return sfis[i].tagged
|
||||
}
|
||||
return byIndex(sfis).Less(i, j)
|
||||
})
|
||||
|
||||
// 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 := sfis[:0]
|
||||
for advance, i := 0, 0; i < len(sfis); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of sfis with the name of this first field.
|
||||
sfi := sfis[i]
|
||||
name := sfi.name
|
||||
for advance = 1; i+advance < len(sfis); advance++ {
|
||||
fj := sfis[i+advance]
|
||||
if fj.name != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, sfi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(sfis[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func getStructFieldInfoSlice(rootType reflect.Type) []structFieldInfo {
|
||||
sfis := getStructFieldInfo(rootType)
|
||||
|
||||
sort.Sort(byIndex(sfis))
|
||||
|
||||
return sfis
|
||||
}
|
||||
|
||||
func getStructFieldInfoMap(rootType reflect.Type) structFieldMap {
|
||||
sfis := getStructFieldInfo(rootType)
|
||||
|
||||
out := structFieldMap{}
|
||||
for _, elem := range sfis {
|
||||
out.insert(elem)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *hjsonEncoder) writeFields(
|
||||
fis []fieldInfo,
|
||||
noIndent bool,
|
||||
separator string,
|
||||
isRootObject bool,
|
||||
isObjElement bool,
|
||||
cm Comments,
|
||||
) error {
|
||||
indent1 := e.indent
|
||||
if !isRootObject || e.EmitRootBraces || len(fis) == 0 {
|
||||
e.bracesIndent(isObjElement, len(fis) == 0, cm, separator)
|
||||
e.WriteString("{" + cm.InsideFirst)
|
||||
|
||||
if len(fis) == 0 {
|
||||
if cm.InsideFirst != "" || cm.InsideLast != "" {
|
||||
e.WriteString(e.Eol)
|
||||
}
|
||||
e.WriteString(cm.InsideLast)
|
||||
if cm.InsideLast != "" {
|
||||
endsInsideComment, endsWithLineFeed := investigateComment(cm.InsideLast)
|
||||
if endsInsideComment {
|
||||
e.writeIndent(e.indent)
|
||||
}
|
||||
if endsWithLineFeed {
|
||||
e.writeIndentNoEOL(e.indent)
|
||||
}
|
||||
} else if cm.InsideFirst != "" {
|
||||
e.writeIndentNoEOL(e.indent)
|
||||
}
|
||||
e.WriteString("}")
|
||||
return nil
|
||||
}
|
||||
|
||||
e.indent++
|
||||
} else {
|
||||
e.WriteString(cm.InsideFirst)
|
||||
}
|
||||
|
||||
// Join all of the member texts together, separated with newlines
|
||||
var elemCm Comments
|
||||
for i, fi := range fis {
|
||||
var elem reflect.Value
|
||||
elem, elemCm = e.unpackNode(fi.field, elemCm)
|
||||
if i > 0 || !isRootObject || e.EmitRootBraces {
|
||||
e.WriteString(e.Eol)
|
||||
}
|
||||
if len(fi.comment) > 0 {
|
||||
for _, line := range strings.Split(fi.comment, e.Eol) {
|
||||
e.writeIndentNoEOL(e.indent)
|
||||
e.WriteString(fmt.Sprintf("# %s\n", line))
|
||||
}
|
||||
}
|
||||
if elemCm.Before == "" {
|
||||
e.writeIndentNoEOL(e.indent)
|
||||
} else {
|
||||
e.WriteString(elemCm.Before)
|
||||
}
|
||||
e.WriteString(e.quoteName(fi.name))
|
||||
e.WriteString(":")
|
||||
e.WriteString(elemCm.Key)
|
||||
|
||||
if err := e.str(elem, false, " ", false, true, elemCm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fi.comment) > 0 && i < len(fis)-1 {
|
||||
e.WriteString(e.Eol)
|
||||
}
|
||||
|
||||
e.WriteString(elemCm.After)
|
||||
}
|
||||
|
||||
if cm.InsideLast != "" {
|
||||
e.WriteString(e.Eol + cm.InsideLast)
|
||||
}
|
||||
|
||||
if !isRootObject || e.EmitRootBraces {
|
||||
if cm.InsideLast == "" {
|
||||
e.writeIndent(indent1)
|
||||
} else {
|
||||
endsInsideComment, endsWithLineFeed := investigateComment(cm.InsideLast)
|
||||
if endsInsideComment {
|
||||
e.writeIndent(indent1)
|
||||
} else if endsWithLineFeed {
|
||||
e.writeIndentNoEOL(indent1)
|
||||
}
|
||||
}
|
||||
e.WriteString("}")
|
||||
}
|
||||
|
||||
e.indent = indent1
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user