mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-10 20:05:50 +02:00
Use refs in jsonschema userconfig generator
This makes it possible to use recursive structures in the user config.
This commit is contained in:
parent
62c6ba7d57
commit
30e9bf8a75
11 changed files with 1542 additions and 1795 deletions
|
@ -411,6 +411,11 @@ os:
|
|||
# window is closed.
|
||||
editAtLineAndWait: ""
|
||||
|
||||
# Whether lazygit suspends until an edit process returns
|
||||
# Pointer to bool so that we can distinguish unset (nil) from false.
|
||||
# We're naming this `editInTerminal` for backwards compatibility
|
||||
editInTerminal: false
|
||||
|
||||
# For opening a directory in an editor
|
||||
openDirInEditor: ""
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -11,7 +11,6 @@ require (
|
|||
github.com/gdamore/tcell/v2 v2.8.1
|
||||
github.com/go-errors/errors v1.5.1
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/iancoleman/orderedmap v0.3.0
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
|
|
2
go.sum
2
go.sum
|
@ -171,8 +171,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
|||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
|
||||
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
|
|
|
@ -7,41 +7,76 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/karimkhaleel/jsonschema"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func GetSchemaDir() string {
|
||||
return utils.GetLazyRootDirectory() + "/schema"
|
||||
}
|
||||
|
||||
func GenerateSchema() {
|
||||
func GenerateSchema() *jsonschema.Schema {
|
||||
schema := customReflect(&config.UserConfig{})
|
||||
obj, _ := json.MarshalIndent(schema, "", " ")
|
||||
obj = append(obj, '\n')
|
||||
|
||||
if err := os.WriteFile(GetSchemaDir()+"/config.json", obj, 0o644); err != nil {
|
||||
fmt.Println("Error writing to file:", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
func getSubSchema(rootSchema, parentSchema *jsonschema.Schema, key string) *jsonschema.Schema {
|
||||
subSchema, found := parentSchema.Properties.Get(key)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("Failed to find subSchema at %s on parent", key))
|
||||
}
|
||||
|
||||
// This means the schema is defined on the rootSchema's Definitions
|
||||
if subSchema.Ref != "" {
|
||||
key, _ = strings.CutPrefix(subSchema.Ref, "#/$defs/")
|
||||
refSchema, ok := rootSchema.Definitions[key]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Failed to find #/$defs/%s", key))
|
||||
}
|
||||
refSchema.Description = subSchema.Description
|
||||
return refSchema
|
||||
}
|
||||
|
||||
return subSchema
|
||||
}
|
||||
|
||||
func customReflect(v *config.UserConfig) *jsonschema.Schema {
|
||||
defaultConfig := config.GetDefaultConfig()
|
||||
r := &jsonschema.Reflector{FieldNameTag: "yaml", RequiredFromJSONSchemaTags: true, DoNotReference: true}
|
||||
r := &jsonschema.Reflector{FieldNameTag: "yaml", RequiredFromJSONSchemaTags: true}
|
||||
if err := r.AddGoComments("github.com/jesseduffield/lazygit/pkg/config", "../config"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
schema := r.Reflect(v)
|
||||
defaultConfig := config.GetDefaultConfig()
|
||||
userConfigSchema := schema.Definitions["UserConfig"]
|
||||
|
||||
setDefaultVals(defaultConfig, schema)
|
||||
defaultValue := reflect.ValueOf(defaultConfig).Elem()
|
||||
|
||||
yamlToFieldNames := lo.Invert(userConfigSchema.OriginalPropertiesMapping)
|
||||
|
||||
for pair := userConfigSchema.Properties.Oldest(); pair != nil; pair = pair.Next() {
|
||||
yamlName := pair.Key
|
||||
fieldName := yamlToFieldNames[yamlName]
|
||||
|
||||
subSchema := getSubSchema(schema, userConfigSchema, yamlName)
|
||||
|
||||
setDefaultVals(schema, subSchema, defaultValue.FieldByName(fieldName).Interface())
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
||||
func setDefaultVals(rootSchema, schema *jsonschema.Schema, defaults any) {
|
||||
t := reflect.TypeOf(defaults)
|
||||
v := reflect.ValueOf(defaults)
|
||||
|
||||
|
@ -50,6 +85,24 @@ func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
|||
v = v.Elem()
|
||||
}
|
||||
|
||||
k := t.Kind()
|
||||
_ = k
|
||||
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
schema.Default = v.Bool()
|
||||
case reflect.Int:
|
||||
schema.Default = v.Int()
|
||||
case reflect.String:
|
||||
schema.Default = v.String()
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
value := v.Field(i).Interface()
|
||||
parentKey := t.Field(i).Name
|
||||
|
@ -59,13 +112,10 @@ func setDefaultVals(defaults any, schema *jsonschema.Schema) {
|
|||
continue
|
||||
}
|
||||
|
||||
subSchema, ok := schema.Properties.Get(key)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
subSchema := getSubSchema(rootSchema, schema, key)
|
||||
|
||||
if isStruct(value) {
|
||||
setDefaultVals(value, subSchema)
|
||||
setDefaultVals(rootSchema, subSchema, value)
|
||||
} else if !isZeroValue(value) {
|
||||
subSchema.Default = value
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@ package jsonschema
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/iancoleman/orderedmap"
|
||||
"github.com/jesseduffield/lazycore/pkg/utils"
|
||||
"github.com/karimkhaleel/jsonschema"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -106,16 +105,7 @@ func (n *Node) MarshalYAML() (interface{}, error) {
|
|||
setComment(&keyNode, n.Description)
|
||||
}
|
||||
|
||||
if n.Default != nil {
|
||||
valueNode := yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
}
|
||||
err := valueNode.Encode(n.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &valueNode)
|
||||
} else if len(n.Children) > 0 {
|
||||
if len(n.Children) > 0 {
|
||||
childrenNode := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
}
|
||||
|
@ -136,62 +126,20 @@ func (n *Node) MarshalYAML() (interface{}, error) {
|
|||
childrenNode.Content = append(childrenNode.Content, childYaml.(*yaml.Node).Content...)
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &childrenNode)
|
||||
} else {
|
||||
valueNode := yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
}
|
||||
err := valueNode.Encode(n.Default)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node.Content = append(node.Content, &keyNode, &valueNode)
|
||||
}
|
||||
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
func getDescription(v *orderedmap.OrderedMap) string {
|
||||
description, ok := v.Get("description")
|
||||
if !ok {
|
||||
description = ""
|
||||
}
|
||||
return description.(string)
|
||||
}
|
||||
|
||||
func getDefault(v *orderedmap.OrderedMap) (error, any) {
|
||||
defaultValue, ok := v.Get("default")
|
||||
if ok {
|
||||
return nil, defaultValue
|
||||
}
|
||||
|
||||
dataType, ok := v.Get("type")
|
||||
if ok {
|
||||
dataTypeString := dataType.(string)
|
||||
if dataTypeString == "string" {
|
||||
return nil, ""
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Failed to get default value"), nil
|
||||
}
|
||||
|
||||
func parseNode(parent *Node, name string, value *orderedmap.OrderedMap) {
|
||||
description := getDescription(value)
|
||||
err, defaultValue := getDefault(value)
|
||||
if err == nil {
|
||||
leaf := &Node{Name: name, Description: description, Default: defaultValue}
|
||||
parent.Children = append(parent.Children, leaf)
|
||||
}
|
||||
|
||||
properties, ok := value.Get("properties")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
orderedProperties := properties.(orderedmap.OrderedMap)
|
||||
|
||||
node := &Node{Name: name, Description: description}
|
||||
parent.Children = append(parent.Children, node)
|
||||
|
||||
keys := orderedProperties.Keys()
|
||||
for _, name := range keys {
|
||||
value, _ := orderedProperties.Get(name)
|
||||
typedValue := value.(orderedmap.OrderedMap)
|
||||
parseNode(node, name, &typedValue)
|
||||
}
|
||||
}
|
||||
|
||||
func writeToConfigDocs(config []byte) error {
|
||||
configPath := utils.GetLazyRootDirectory() + "/docs/Config.md"
|
||||
markdown, err := os.ReadFile(configPath)
|
||||
|
@ -222,31 +170,12 @@ func writeToConfigDocs(config []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func GenerateConfigDocs() {
|
||||
content, err := os.ReadFile(GetSchemaDir() + "/config.json")
|
||||
if err != nil {
|
||||
panic("Error reading config.json")
|
||||
func GenerateConfigDocs(schema *jsonschema.Schema) {
|
||||
rootNode := &Node{
|
||||
Children: make([]*Node, 0),
|
||||
}
|
||||
|
||||
schema := orderedmap.New()
|
||||
|
||||
err = json.Unmarshal(content, &schema)
|
||||
if err != nil {
|
||||
panic("Failed to unmarshal config.json")
|
||||
}
|
||||
|
||||
root, ok := schema.Get("properties")
|
||||
if !ok {
|
||||
panic("properties key not found in schema")
|
||||
}
|
||||
orderedRoot := root.(orderedmap.OrderedMap)
|
||||
|
||||
rootNode := Node{}
|
||||
for _, name := range orderedRoot.Keys() {
|
||||
value, _ := orderedRoot.Get(name)
|
||||
typedValue := value.(orderedmap.OrderedMap)
|
||||
parseNode(&rootNode, name, &typedValue)
|
||||
}
|
||||
recurseOverSchema(schema, schema.Definitions["UserConfig"], rootNode)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
encoder := yaml.NewEncoder(&buffer)
|
||||
|
@ -262,8 +191,51 @@ func GenerateConfigDocs() {
|
|||
|
||||
config := prepareMarshalledConfig(buffer)
|
||||
|
||||
err = writeToConfigDocs(config)
|
||||
err := writeToConfigDocs(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func recurseOverSchema(rootSchema, schema *jsonschema.Schema, parent *Node) {
|
||||
if schema == nil || schema.Properties == nil || schema.Properties.Len() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for pair := schema.Properties.Oldest(); pair != nil; pair = pair.Next() {
|
||||
subSchema := getSubSchema(rootSchema, schema, pair.Key)
|
||||
|
||||
// Skip empty objects
|
||||
if subSchema.Type == "object" && subSchema.Properties == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip empty arrays
|
||||
if isZeroValue(subSchema.Default) && subSchema.Type == "array" {
|
||||
continue
|
||||
}
|
||||
|
||||
node := Node{
|
||||
Name: pair.Key,
|
||||
Description: subSchema.Description,
|
||||
Default: getZeroValue(subSchema.Default, subSchema.Type),
|
||||
}
|
||||
parent.Children = append(parent.Children, &node)
|
||||
recurseOverSchema(rootSchema, subSchema, &node)
|
||||
}
|
||||
}
|
||||
|
||||
func getZeroValue(val any, t string) any {
|
||||
if !isZeroValue(val) {
|
||||
return val
|
||||
}
|
||||
|
||||
switch t {
|
||||
case "string":
|
||||
return ""
|
||||
case "boolean":
|
||||
return false
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ import (
|
|||
|
||||
func main() {
|
||||
fmt.Printf("Generating jsonschema in %s...\n", jsonschema.GetSchemaDir())
|
||||
jsonschema.GenerateSchema()
|
||||
jsonschema.GenerateConfigDocs()
|
||||
schema := jsonschema.GenerateSchema()
|
||||
jsonschema.GenerateConfigDocs(schema)
|
||||
}
|
||||
|
|
2736
schema/config.json
2736
schema/config.json
File diff suppressed because it is too large
Load diff
21
vendor/github.com/iancoleman/orderedmap/LICENSE
generated
vendored
21
vendor/github.com/iancoleman/orderedmap/LICENSE
generated
vendored
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Ian Coleman
|
||||
|
||||
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.
|
266
vendor/github.com/iancoleman/orderedmap/orderedmap.go
generated
vendored
266
vendor/github.com/iancoleman/orderedmap/orderedmap.go
generated
vendored
|
@ -1,266 +0,0 @@
|
|||
package orderedmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Pair struct {
|
||||
key string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
func (kv *Pair) Key() string {
|
||||
return kv.key
|
||||
}
|
||||
|
||||
func (kv *Pair) Value() interface{} {
|
||||
return kv.value
|
||||
}
|
||||
|
||||
type ByPair struct {
|
||||
Pairs []*Pair
|
||||
LessFunc func(a *Pair, j *Pair) bool
|
||||
}
|
||||
|
||||
func (a ByPair) Len() int { return len(a.Pairs) }
|
||||
func (a ByPair) Swap(i, j int) { a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i] }
|
||||
func (a ByPair) Less(i, j int) bool { return a.LessFunc(a.Pairs[i], a.Pairs[j]) }
|
||||
|
||||
type OrderedMap struct {
|
||||
keys []string
|
||||
values map[string]interface{}
|
||||
escapeHTML bool
|
||||
}
|
||||
|
||||
func New() *OrderedMap {
|
||||
o := OrderedMap{}
|
||||
o.keys = []string{}
|
||||
o.values = map[string]interface{}{}
|
||||
o.escapeHTML = true
|
||||
return &o
|
||||
}
|
||||
|
||||
func (o *OrderedMap) SetEscapeHTML(on bool) {
|
||||
o.escapeHTML = on
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Get(key string) (interface{}, bool) {
|
||||
val, exists := o.values[key]
|
||||
return val, exists
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Set(key string, value interface{}) {
|
||||
_, exists := o.values[key]
|
||||
if !exists {
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
o.values[key] = value
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Delete(key string) {
|
||||
// check key is in use
|
||||
_, ok := o.values[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
// remove from keys
|
||||
for i, k := range o.keys {
|
||||
if k == key {
|
||||
o.keys = append(o.keys[:i], o.keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
// remove from values
|
||||
delete(o.values, key)
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Keys() []string {
|
||||
return o.keys
|
||||
}
|
||||
|
||||
func (o *OrderedMap) Values() map[string]interface{} {
|
||||
return o.values
|
||||
}
|
||||
|
||||
// SortKeys Sort the map keys using your sort func
|
||||
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
|
||||
sortFunc(o.keys)
|
||||
}
|
||||
|
||||
// Sort Sort the map using your sort func
|
||||
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
|
||||
pairs := make([]*Pair, len(o.keys))
|
||||
for i, key := range o.keys {
|
||||
pairs[i] = &Pair{key, o.values[key]}
|
||||
}
|
||||
|
||||
sort.Sort(ByPair{pairs, lessFunc})
|
||||
|
||||
for i, pair := range pairs {
|
||||
o.keys[i] = pair.key
|
||||
}
|
||||
}
|
||||
|
||||
func (o *OrderedMap) UnmarshalJSON(b []byte) error {
|
||||
if o.values == nil {
|
||||
o.values = map[string]interface{}{}
|
||||
}
|
||||
err := json.Unmarshal(b, &o.values)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
if _, err = dec.Token(); err != nil { // skip '{'
|
||||
return err
|
||||
}
|
||||
o.keys = make([]string, 0, len(o.values))
|
||||
return decodeOrderedMap(dec, o)
|
||||
}
|
||||
|
||||
func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error {
|
||||
hasKey := make(map[string]bool, len(o.values))
|
||||
for {
|
||||
token, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok && delim == '}' {
|
||||
return nil
|
||||
}
|
||||
key := token.(string)
|
||||
if hasKey[key] {
|
||||
// duplicate key
|
||||
for j, k := range o.keys {
|
||||
if k == key {
|
||||
copy(o.keys[j:], o.keys[j+1:])
|
||||
break
|
||||
}
|
||||
}
|
||||
o.keys[len(o.keys)-1] = key
|
||||
} else {
|
||||
hasKey[key] = true
|
||||
o.keys = append(o.keys, key)
|
||||
}
|
||||
|
||||
token, err = dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok {
|
||||
switch delim {
|
||||
case '{':
|
||||
if values, ok := o.values[key].(map[string]interface{}); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(values)),
|
||||
values: values,
|
||||
escapeHTML: o.escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
o.values[key] = newMap
|
||||
} else if oldMap, ok := o.values[key].(OrderedMap); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(oldMap.values)),
|
||||
values: oldMap.values,
|
||||
escapeHTML: o.escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
o.values[key] = newMap
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
case '[':
|
||||
if values, ok := o.values[key].([]interface{}); ok {
|
||||
if err = decodeSlice(dec, values, o.escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, o.escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
|
||||
for index := 0; ; index++ {
|
||||
token, err := dec.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if delim, ok := token.(json.Delim); ok {
|
||||
switch delim {
|
||||
case '{':
|
||||
if index < len(s) {
|
||||
if values, ok := s[index].(map[string]interface{}); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(values)),
|
||||
values: values,
|
||||
escapeHTML: escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
s[index] = newMap
|
||||
} else if oldMap, ok := s[index].(OrderedMap); ok {
|
||||
newMap := OrderedMap{
|
||||
keys: make([]string, 0, len(oldMap.values)),
|
||||
values: oldMap.values,
|
||||
escapeHTML: escapeHTML,
|
||||
}
|
||||
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
||||
return err
|
||||
}
|
||||
s[index] = newMap
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
||||
return err
|
||||
}
|
||||
case '[':
|
||||
if index < len(s) {
|
||||
if values, ok := s[index].([]interface{}); ok {
|
||||
if err = decodeSlice(dec, values, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
||||
return err
|
||||
}
|
||||
case ']':
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o OrderedMap) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('{')
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetEscapeHTML(o.escapeHTML)
|
||||
for i, k := range o.keys {
|
||||
if i > 0 {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
// add key
|
||||
if err := encoder.Encode(k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.WriteByte(':')
|
||||
// add value
|
||||
if err := encoder.Encode(o.values[k]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
return buf.Bytes(), nil
|
||||
}
|
81
vendor/github.com/iancoleman/orderedmap/readme.md
generated
vendored
81
vendor/github.com/iancoleman/orderedmap/readme.md
generated
vendored
|
@ -1,81 +0,0 @@
|
|||
# orderedmap
|
||||
|
||||
[](https://travis-ci.com/iancoleman/orderedmap)
|
||||
|
||||
A golang data type equivalent to python's collections.OrderedDict
|
||||
|
||||
Retains order of keys in maps
|
||||
|
||||
Can be JSON serialized / deserialized
|
||||
|
||||
# Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/iancoleman/orderedmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// use New() instead of o := map[string]interface{}{}
|
||||
o := orderedmap.New()
|
||||
|
||||
// use SetEscapeHTML() to whether escape problematic HTML characters or not, defaults is true
|
||||
o.SetEscapeHTML(false)
|
||||
|
||||
// use Set instead of o["a"] = 1
|
||||
o.Set("a", 1)
|
||||
|
||||
// add some value with special characters
|
||||
o.Set("b", "\\.<>[]{}_-")
|
||||
|
||||
// use Get instead of i, ok := o["a"]
|
||||
val, ok := o.Get("a")
|
||||
|
||||
// use Keys instead of for k, v := range o
|
||||
keys := o.Keys()
|
||||
for _, k := range keys {
|
||||
v, _ := o.Get(k)
|
||||
}
|
||||
|
||||
// use o.Delete instead of delete(o, key)
|
||||
o.Delete("a")
|
||||
|
||||
// serialize to a json string using encoding/json
|
||||
bytes, err := json.Marshal(o)
|
||||
prettyBytes, err := json.MarshalIndent(o, "", " ")
|
||||
|
||||
// deserialize a json string using encoding/json
|
||||
// all maps (including nested maps) will be parsed as orderedmaps
|
||||
s := `{"a": 1}`
|
||||
err := json.Unmarshal([]byte(s), &o)
|
||||
|
||||
// sort the keys
|
||||
o.SortKeys(sort.Strings)
|
||||
|
||||
// sort by Pair
|
||||
o.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
|
||||
return a.Value().(float64) < b.Value().(float64)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
# Caveats
|
||||
|
||||
* OrderedMap only takes strings for the key, as per [the JSON spec](http://json.org/).
|
||||
|
||||
# Tests
|
||||
|
||||
```
|
||||
go test
|
||||
```
|
||||
|
||||
# Alternatives
|
||||
|
||||
None of the alternatives offer JSON serialization.
|
||||
|
||||
* [cevaris/ordered_map](https://github.com/cevaris/ordered_map)
|
||||
* [mantyr/iterator](https://github.com/mantyr/iterator)
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -109,9 +109,6 @@ github.com/gobwas/glob/util/strings
|
|||
# github.com/gookit/color v1.4.2
|
||||
## explicit; go 1.12
|
||||
github.com/gookit/color
|
||||
# github.com/iancoleman/orderedmap v0.3.0
|
||||
## explicit; go 1.16
|
||||
github.com/iancoleman/orderedmap
|
||||
# github.com/imdario/mergo v0.3.11
|
||||
## explicit; go 1.13
|
||||
github.com/imdario/mergo
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue