diff --git a/README.md b/README.md index 6c2a8b2..d92344b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Go Pkg](https://img.shields.io/github/release/hjson/hjson-go.svg?style=flat-square&label=go-pkg)](https://github.com/hjson/hjson-go/releases) [![Go Report Card](https://goreportcard.com/badge/github.com/hjson/hjson-go?style=flat-square)](https://goreportcard.com/report/github.com/hjson/hjson-go) [![coverage](https://img.shields.io/badge/coverage-ok-brightgreen.svg?style=flat-square)](https://gocover.io/github.com/hjson/hjson-go/) -[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/hjson/hjson-go/v4) +[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/bingoohuang/hjson) ![Hjson Intro](https://hjson.github.io/hjson1.gif) @@ -29,7 +29,7 @@ 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 +More documentation can be found at https://pkg.go.dev/github.com/bingoohuang/hjson # Install @@ -39,11 +39,11 @@ If you instead want to build locally, make sure you have a working Go environmen - 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" +import "github.com/bingoohuang/hjson" ``` - 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 +go install github.com/bingoohuang/hjson/hjson-cli@latest ``` # Usage as command line tool ``` @@ -81,7 +81,7 @@ Sample: package main import ( - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" "fmt" ) @@ -142,7 +142,7 @@ If you prefer, you can also unmarshal to Go structs (including structs implement package main import ( - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" "fmt" ) @@ -193,7 +193,7 @@ By using key `comment` in struct field tags you can specify comments to be writt package main import ( - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" "fmt" ) @@ -256,7 +256,7 @@ package main import ( "fmt" - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" ) func main() { @@ -324,7 +324,7 @@ The ambiguity can be avoided by using typed destinations when unmarshalling. A s package main import ( - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" "fmt" ) @@ -370,7 +370,7 @@ In order to avoid stack overflow all unmarshal-functions return an error if the # API -[![godoc](https://godoc.org/github.com/hjson/hjson-go/v4?status.svg)](https://godoc.org/github.com/hjson/hjson-go/v4) +[![godoc](https://godoc.org/github.com/bingoohuang/hjson?status.svg)](https://godoc.org/github.com/bingoohuang/hjson) # History diff --git a/build_release.sh b/build_release.sh index bab1bca..0aa7ca5 100755 --- a/build_release.sh +++ b/build_release.sh @@ -17,7 +17,7 @@ function build() { 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 + go build -ldflags "-w -s -X main.Version=${VERSION}" github.com/bingoohuang/hjson/hjson-cli if [[ $3 == "zip" ]]; then mv $OUT/hjson-cli.exe $OUT/hjson.exe zip -j ${OUT}.zip $OUT/* diff --git a/color.go b/color.go new file mode 100644 index 0000000..7cb5e43 --- /dev/null +++ b/color.go @@ -0,0 +1,42 @@ +package hjson + +// Style is the color style +type Style struct { + Key, String, Number [2]string + True, False, Null [2]string + Escape [2]string + Remark [2]string + Append func(dst []byte, c byte) []byte +} + +// TerminalStyle is for terminals +var TerminalStyle *Style + +func init() { + TerminalStyle = &Style{ + Key: [2]string{"\x1B[94m", "\x1B[0m"}, + String: [2]string{"\x1B[92m", "\x1B[0m"}, + Number: [2]string{"\x1B[93m", "\x1B[0m"}, + True: [2]string{"\x1B[96m", "\x1B[0m"}, + False: [2]string{"\x1B[96m", "\x1B[0m"}, + Null: [2]string{"\x1B[91m", "\x1B[0m"}, + Escape: [2]string{"\x1B[35m", "\x1B[0m"}, + Remark: [2]string{"\x1B[90m", "\x1B[0m"}, + Append: func(dst []byte, c byte) []byte { + if c < ' ' && (c != '\r' && c != '\n' && c != '\t' && c != '\v') { + dst = append(dst, "\\u00"...) + dst = append(dst, hexp((c>>4)&0xF)) + return append(dst, hexp((c)&0xF)) + } + return append(dst, c) + }, + } +} +func hexp(p byte) byte { + switch { + case p < 10: + return p + '0' + default: + return (p - 10) + 'a' + } +} diff --git a/decode.go b/decode.go index 06b0a16..344921d 100644 --- a/decode.go +++ b/decode.go @@ -716,14 +716,14 @@ func (p *hjsonParser) readObject( elemType = newDestType if newDest.IsValid() { - if newDest.Kind() != reflect.Struct { + if newDest.Kind() == reflect.Struct { + newDest = newDest.Field(i) + } else { // We are only keeping track of newDest in case it contains a // tree that we will partially update. But here we have not found // any tree, so we can ignore newDest and just look at // newDestType instead. newDest = reflect.Value{} - } else { - newDest = newDest.Field(i) } } } @@ -872,47 +872,43 @@ func (p *hjsonParser) rootValue(dest reflect.Value) (ret interface{}, err error) return } - if ret == nil { - // Assume we have a root object without braces. - ret, errSyntax = p.readObject(true, dest, t, ciBefore) - ciAfter, err = p.checkTrailing() - if errSyntax != nil || err != nil { - // Syntax error, or maybe a single JSON value. - ret = nil - err = nil - } else { - if p.nodeDestination { - if node, ok := ret.(*Node); ok { - p.setComment1(&node.Cm.After, ciAfter) - } + // Assume we have a root object without braces. + ret, errSyntax = p.readObject(true, dest, t, ciBefore) + ciAfter, err = p.checkTrailing() + if errSyntax != nil || err != nil { + // Syntax error, or maybe a single JSON value. + ret = nil + err = nil + } else { + if p.nodeDestination { + if node, ok := ret.(*Node); ok { + p.setComment1(&node.Cm.After, ciAfter) } - return } + return } - if ret == nil { - // test if we are dealing with a single JSON value instead (true/false/null/num/"") - p.resetAt() - ret, err = p.readValue(dest, t) - if err == nil { - ciAfter, err = p.checkTrailing() - } - if err == nil { - if p.nodeDestination { - if node, ok := ret.(*Node); ok { - // ciBefore has been read again and set on the node inside the - // function p.readValue(). - existingAfter := node.Cm.After - p.setComment1(&node.Cm.After, ciAfter) - if node.Cm.After != "" { - existingAfter += "\n" - } - node.Cm.After = existingAfter + node.Cm.After + // test if we are dealing with a single JSON value instead (true/false/null/num/"") + p.resetAt() + ret, err = p.readValue(dest, t) + if err == nil { + ciAfter, err = p.checkTrailing() + } + if err == nil { + if p.nodeDestination { + if node, ok := ret.(*Node); ok { + // ciBefore has been read again and set on the node inside the + // function p.readValue(). + existingAfter := node.Cm.After + p.setComment1(&node.Cm.After, ciAfter) + if node.Cm.After != "" { + existingAfter += "\n" } + node.Cm.After = existingAfter + node.Cm.After } - - return } + + return } if errSyntax != nil { @@ -950,7 +946,7 @@ func orderedUnmarshal( ) { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr || rv.IsNil() { - return nil, fmt.Errorf("Cannot unmarshal into non-pointer %v", reflect.TypeOf(v)) + return nil, fmt.Errorf("cannot unmarshal into non-pointer %v", reflect.TypeOf(v)) } parser := &hjsonParser{ @@ -1021,7 +1017,7 @@ func UnmarshalWithOptions(data []byte, v interface{}, options DecoderOptions) er *inOM = *outOM return nil } - return fmt.Errorf("Cannot unmarshal into hjson.OrderedMap: Try %v as destination instead", + return fmt.Errorf("cannot unmarshal into hjson.OrderedMap: Try %v as destination instead", reflect.TypeOf(v)) } @@ -1037,7 +1033,7 @@ func UnmarshalWithOptions(data []byte, v interface{}, options DecoderOptions) er // and merging. buf, err := json.Marshal(value) if err != nil { - return errors.New("Internal error") + return errors.New("internal error") } dec := json.NewDecoder(bytes.NewBuffer(buf)) diff --git a/encode.go b/encode.go index 1245cdd..5ed1980 100644 --- a/encode.go +++ b/encode.go @@ -34,6 +34,11 @@ type EncoderOptions struct { // Write comments, if any are found in hjson.Node structs or as tags on // other structs. Comments bool + + // EnableColor enables colorized output + EnableColor bool + // ColorStyle is the style to use for colorized output + ColorStyle *Style } // DefaultOptions returns the default encoding options. @@ -55,6 +60,8 @@ func DefaultOptions() EncoderOptions { IndentBy: " ", BaseIndentation: "", Comments: true, + EnableColor: false, + ColorStyle: TerminalStyle, } } @@ -123,14 +130,18 @@ func (e *hjsonEncoder) quoteForComment(cmStr string) bool { return false } -func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool, +func (e *hjsonEncoder) quote(value, 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) + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.String[0], e.ColorStyle.String[1] + } if len(value) == 0 { - e.WriteString(separator + `""`) + e.WriteString(separator + l + `""` + r) } else if e.QuoteAlways || hasCommentAfter || needsQuotes.MatchString(value) || @@ -144,26 +155,27 @@ func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool, // sequences. if !needsEscape.MatchString(value) { - e.WriteString(separator + `"` + value + `"`) + + e.WriteString(separator + l + `"` + value + `"` + r) } else if !needsEscapeML.MatchString(value) && !isRootObject { - e.mlString(value, separator, keyComment) + e.mlString(value, separator, keyComment, l, r) } else { - e.WriteString(separator + `"` + e.quoteReplace(value) + `"`) + e.WriteString(separator + l + `"` + e.quoteReplace(value) + `"` + r) } } else { // return without quotes - e.WriteString(separator + value) + e.WriteString(separator + l + value + r) } } -func (e *hjsonEncoder) mlString(value string, separator string, keyComment string) { +func (e *hjsonEncoder) mlString(value, separator, keyComment, lColor, rColor 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(separator + lColor + "'''") e.WriteString(a[0]) } else { if !strings.Contains(keyComment, "\n") { @@ -176,11 +188,11 @@ func (e *hjsonEncoder) mlString(value string, separator string, keyComment strin indent = 0 } e.writeIndent(indent) - e.WriteString(v) + e.WriteString(lColor + v + rColor) } e.writeIndent(e.indent + 1) } - e.WriteString("'''") + e.WriteString("'''" + rColor) } func (e *hjsonEncoder) quoteName(name string) string { @@ -279,6 +291,14 @@ func (e *hjsonEncoder) unpackNode(value reflect.Value, cm Comments) (reflect.Val return value, cm } +func (e *hjsonEncoder) writeNull() { + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Null[0], e.ColorStyle.Null[1] + } + e.WriteString(l + "null" + r) +} + // This function can often be called from within itself, so do not output // anything from the upper half of it. func (e *hjsonEncoder) str( @@ -319,14 +339,14 @@ func (e *hjsonEncoder) str( if !value.IsValid() { e.WriteString(separator) - e.WriteString("null") + e.writeNull() return nil } if kind == reflect.Interface || kind == reflect.Ptr { if value.IsNil() { e.WriteString(separator) - e.WriteString("null") + e.writeNull() return nil } return e.str(value.Elem(), noIndent, separator, isRootObject, isObjElement, cm) @@ -367,8 +387,14 @@ func (e *hjsonEncoder) str( if n == "" { n = "0" } + e.WriteString(separator) + + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Number[0], e.ColorStyle.Number[1] + } // without quotes - e.WriteString(separator + n) + e.WriteString(l + n + r) } else { e.quote(value.String(), separator, isRootObject, cm.Key, e.quoteForComment(cm.After)) @@ -376,21 +402,33 @@ func (e *hjsonEncoder) str( case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: e.WriteString(separator) - e.WriteString(strconv.FormatInt(value.Int(), 10)) + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Number[0], e.ColorStyle.Number[1] + } + e.WriteString(l + strconv.FormatInt(value.Int(), 10) + r) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: e.WriteString(separator) - e.WriteString(strconv.FormatUint(value.Uint(), 10)) + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Number[0], e.ColorStyle.Number[1] + } + e.WriteString(l + strconv.FormatUint(value.Uint(), 10) + r) case reflect.Float32, reflect.Float64: // JSON numbers must be finite. Encode non-finite numbers as null. e.WriteString(separator) number := value.Float() + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Number[0], e.ColorStyle.Number[1] + } if math.IsInf(number, 0) || math.IsNaN(number) { - e.WriteString("null") + e.writeNull() } else if number == -0 { - e.WriteString("0") + e.WriteString(l + "0" + r) } else { // find shortest representation ('G' does not work) val := strconv.FormatFloat(number, 'f', -1, 64) @@ -398,15 +436,23 @@ func (e *hjsonEncoder) str( if len(exp) < len(val) { val = strings.ToLower(exp) } - e.WriteString(val) + + e.WriteString(l + val + r) } case reflect.Bool: e.WriteString(separator) + l, r := "", "" if value.Bool() { - e.WriteString("true") + if e.EnableColor { + l, r = e.ColorStyle.True[0], e.ColorStyle.True[1] + } + e.WriteString(l + "true" + r) } else { - e.WriteString("false") + if e.EnableColor { + l, r = e.ColorStyle.False[0], e.ColorStyle.False[1] + } + e.WriteString(l + "false" + r) } case reflect.Slice, reflect.Array: @@ -437,6 +483,10 @@ func (e *hjsonEncoder) str( e.WriteString(e.Eol + elemCm.Before + elemCm.Key) } + if i > 0 && e.Eol == "" { + e.WriteString(", ") + } + if err := e.str(elem, true, "", false, false, elemCm); err != nil { return err } diff --git a/go.mod b/go.mod index 47cab6e..dd6472a 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/hjson/hjson-go/v4 +module github.com/bingoohuang/hjson go 1.12 diff --git a/hjson-cli/main.go b/hjson-cli/main.go index 7f8bb6a..28143b4 100644 --- a/hjson-cli/main.go +++ b/hjson-cli/main.go @@ -9,7 +9,7 @@ import ( "os" "runtime/debug" - "github.com/hjson/hjson-go/v4" + "github.com/bingoohuang/hjson" ) // Can be set when building for example like this: diff --git a/structs.go b/structs.go index 981858a..0280fc6 100644 --- a/structs.go +++ b/structs.go @@ -296,10 +296,17 @@ func (e *hjsonEncoder) writeFields( if i > 0 || !isRootObject || e.EmitRootBraces { e.WriteString(e.Eol) } + if i > 0 && e.Eol == "" { + e.WriteString(", ") + } 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)) + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Remark[0], e.ColorStyle.Remark[1] + } + e.WriteString(fmt.Sprintf("%s# %s%s\n", l, line, r)) } } if elemCm.Before == "" { @@ -307,7 +314,11 @@ func (e *hjsonEncoder) writeFields( } else { e.WriteString(elemCm.Before) } - e.WriteString(e.quoteName(fi.name)) + l, r := "", "" + if e.EnableColor { + l, r = e.ColorStyle.Key[0], e.ColorStyle.Key[1] + } + e.WriteString(l + e.quoteName(fi.name) + r) e.WriteString(":") e.WriteString(elemCm.Key)