Skip to content

Commit

Permalink
Added encoder options (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
trobro authored Apr 19, 2022
1 parent eab10f1 commit e669f6b
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 25 deletions.
73 changes: 49 additions & 24 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ type EncoderOptions struct {
Eol string
// Place braces on the same line
BracesSameLine bool
// Deprecated: Hjson always emits braces
// 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
// Allow the -0 value (unlike ES6)
AllowMinusZero bool
// Encode unknown values as 'null'
Expand All @@ -39,7 +43,9 @@ func DefaultOptions() EncoderOptions {
opt.BracesSameLine = false
opt.EmitRootBraces = true
opt.QuoteAlways = false
opt.QuoteAmbiguousStrings = true
opt.IndentBy = " "
opt.BaseIndentation = ""
opt.AllowMinusZero = false
opt.UnknownAsNull = false
return opt
Expand Down Expand Up @@ -97,9 +103,8 @@ func (e *hjsonEncoder) quote(value string, separator string, isRootObject bool)
if len(value) == 0 {
e.WriteString(separator + `""`)
} else if e.QuoteAlways ||
needsQuotes.MatchString(value) ||
startsWithNumber([]byte(value)) ||
startsWithKeyword.MatchString(value) {
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.
Expand Down Expand Up @@ -178,6 +183,7 @@ func (s sortAlpha) Less(i, j int) bool {

func (e *hjsonEncoder) writeIndent(indent int) {
e.WriteString(e.Eol)
e.WriteString(e.BaseIndentation)
for i := 0; i < indent; i++ {
e.WriteString(e.IndentBy)
}
Expand Down Expand Up @@ -295,29 +301,36 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
}

indent1 := e.indent
e.indent++
if !noIndent && !e.BracesSameLine {
e.writeIndent(indent1)
} else {
e.WriteString(separator)
if !isRootObject || e.EmitRootBraces {
if !noIndent && !e.BracesSameLine {
e.writeIndent(e.indent)
} else {
e.WriteString(separator)
}

e.indent++
e.WriteString("{")
}
e.WriteString("{")

keys := value.MapKeys()
sort.Sort(sortAlpha(keys))

// Join all of the member texts together, separated with newlines
for i := 0; i < len; i++ {
e.writeIndent(e.indent)
if i > 0 || !isRootObject || e.EmitRootBraces {
e.writeIndent(e.indent)
}
e.WriteString(e.quoteName(keys[i].String()))
e.WriteString(":")
if err := e.str(value.MapIndex(keys[i]), false, " ", false); err != nil {
return err
}
}

e.writeIndent(indent1)
e.WriteString("}")
if !isRootObject || e.EmitRootBraces {
e.writeIndent(indent1)
e.WriteString("}")
}
e.indent = indent1

case reflect.Struct:
Expand All @@ -330,13 +343,16 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
}

indent1 := e.indent
e.indent++
if !noIndent && !e.BracesSameLine {
e.writeIndent(indent1)
} else {
e.WriteString(separator)
if !isRootObject || e.EmitRootBraces {
if !noIndent && !e.BracesSameLine {
e.writeIndent(e.indent)
} else {
e.WriteString(separator)
}

e.indent++
e.WriteString("{")
}
e.WriteString("{")

// Join all of the member texts together, separated with newlines
for i := 0; i < l; i++ {
Expand Down Expand Up @@ -366,11 +382,15 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
}
if len(jsonComment) > 0 {
for _, line := range strings.Split(jsonComment, e.Eol) {
e.writeIndent(e.indent)
if i > 0 || !isRootObject || e.EmitRootBraces {
e.writeIndent(e.indent)
}
e.WriteString(fmt.Sprintf("# %s", line))
}
}
e.writeIndent(e.indent)
if i > 0 || !isRootObject || e.EmitRootBraces {
e.writeIndent(e.indent)
}
e.WriteString(e.quoteName(name))
e.WriteString(":")
if err := e.str(curField, false, " ", false); err != nil {
Expand All @@ -381,8 +401,10 @@ func (e *hjsonEncoder) str(value reflect.Value, noIndent bool, separator string,
}
}

e.writeIndent(indent1)
e.WriteString("}")
if !isRootObject || e.EmitRootBraces {
e.writeIndent(indent1)
e.WriteString("}")
}

e.indent = indent1

Expand Down Expand Up @@ -456,10 +478,13 @@ func MarshalWithOptions(v interface{}, options EncoderOptions) ([]byte, error) {
e.indent = 0
e.Eol = options.Eol
e.BracesSameLine = options.BracesSameLine
e.EmitRootBraces = options.EmitRootBraces
e.QuoteAlways = options.QuoteAlways
e.QuoteAmbiguousStrings = options.QuoteAmbiguousStrings
e.IndentBy = options.IndentBy
e.BaseIndentation = options.BaseIndentation

err := e.str(reflect.ValueOf(v), true, "", true)
err := e.str(reflect.ValueOf(v), true, e.BaseIndentation, true)
if err != nil {
return nil, err
}
Expand Down
73 changes: 72 additions & 1 deletion encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,75 @@ func TestEncodeSliceOfPtrOfPtrOfString(t *testing.T) {
]`)) {
t.Error("Marshaler interface error")
}
}
}

func TestNoRootBraces(t *testing.T) {
input := struct {
Foo string
}{
Foo: "Bar",
}
opt := DefaultOptions()
opt.EmitRootBraces = false
buf, err := MarshalWithOptions(input, opt)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(buf, []byte(`Foo: Bar`)) {
t.Error("Encode struct with EmitRootBraces false")
}

theMap := map[string]interface{}{
"Foo": "Bar",
}
buf, err = MarshalWithOptions(theMap, opt)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(buf, []byte(`Foo: Bar`)) {
t.Error("Encode map with EmitRootBraces false")
}
}

func TestBaseIndentation(t *testing.T) {
input := struct {
Foo string
}{
Foo: "Bar",
}
facit := []byte(` {
Foo: Bar
}`)
opt := DefaultOptions()
opt.BaseIndentation = " "
buf, err := MarshalWithOptions(input, opt)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(buf, facit) {
t.Error("Encode with BaseIndentation, comparison:\n", string(buf), "\n", string(facit))
}
}

func TestQuoteAmbiguousStrings(t *testing.T) {
theMap := map[string]interface{}{
"One": "1",
"Null": "null",
"False": "false",
}
facit := []byte(`{
False: false
Null: null
One: 1
}`)
opt := DefaultOptions()
opt.QuoteAlways = false
opt.QuoteAmbiguousStrings = false
buf, err := MarshalWithOptions(theMap, opt)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(buf, facit) {
t.Error("Encode with QuoteAmbiguousStrings false, comparison:\n", string(buf), "\n", string(facit))
}
}

0 comments on commit e669f6b

Please sign in to comment.