summaryrefslogtreecommitdiff
path: root/reencode.go
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@datawire.io>2022-08-17 15:19:12 -0600
committerLuke Shumaker <lukeshu@datawire.io>2022-08-17 15:43:38 -0600
commit494ad195bc31ce6a65f759544355801fe357c56d (patch)
treed7c8b5d59626651a446614666ee02f6a3044e439 /reencode.go
parent25f5b3a2aabe11a7f8dad3d001ac30b65c1e6c06 (diff)
Add more tests around trailing-newlines from the encoder
Diffstat (limited to 'reencode.go')
-rw-r--r--reencode.go56
1 files changed, 45 insertions, 11 deletions
diff --git a/reencode.go b/reencode.go
index bcb3932..4c62cfc 100644
--- a/reencode.go
+++ b/reencode.go
@@ -18,13 +18,24 @@ type ReEncoder struct {
AllowMultipleValues bool
// Whether to minify the JSON.
+ //
+ // Trims all whitespace, except that it emits a newline
+ // between two *number* top-level values (or puts a newline
+ // after all top-level values if ForceTrailingNewlines).
+ //
+ // Trims superflous 0s from numbers.
Compact bool
// String to use to indent; ignored if Compact is true.
+ //
+ // Newlines are emitted *between* top-level values; a newline is
+ // not emitted after the *last* top-level value (unless
+ // ForceTrailingNewlines is on).
Indent string
// String to put before indents.
Prefix string
- // Whether to emit a newline after each top-level value, even
- // if it could unambiguously be omitted.
+ // Whether to emit a newline after each top-level value. See
+ // the comments on Compact and Indent for discussion of how
+ // this is different than the usual behavior.
ForceTrailingNewlines bool
// Returns whether a given character in a string should be
// backslash-escaped. The bool argument is whether it was
@@ -46,6 +57,7 @@ type ReEncoder struct {
// state: .handleRune
lastNonSpace RuneType
+ wasNumber bool
curIndent int
uhex [4]byte // "\uABCD"-encoded characters in strings
fracZeros int64
@@ -79,6 +91,10 @@ func (enc *ReEncoder) Write(p []byte) (int, error) {
return len(p), nil
}
+// Close does what you'd expect, mostly.
+//
+// The *ReEncoder may continue to be written to with new JSON values
+// if enc.AllowMultipleValues is set.
func (enc *ReEncoder) Close() error {
if enc.bufLen > 0 {
return &ReEncodeSyntaxError{
@@ -93,7 +109,7 @@ func (enc *ReEncoder) Close() error {
}
return enc.err
}
- if err := enc.handleRune(0, 0); err != nil {
+ if err := enc.handleRune(0, RuneTypeError); err != nil {
enc.err = &ReEncodeSyntaxError{
Err: err,
Offset: enc.inputPos,
@@ -159,6 +175,22 @@ func (enc *ReEncoder) handleRune(c rune, t RuneType) error {
enc.lastNonSpace = t
}()
+ // emit newlines between top-level values
+ if enc.lastNonSpace == RuneTypeEOF {
+ switch {
+ case enc.wasNumber && t.IsNumber():
+ if err := enc.emitByte('\n'); err != nil {
+ return err
+ }
+ case enc.Indent != "" && !enc.Compact:
+ if err := enc.emitByte('\n'); err != nil {
+ return err
+ }
+ default:
+ // do nothing
+ }
+ }
+
// shorten numbers
switch t { // trim trailing '0's from the fraction-part, but don't remove all digits
case RuneTypeNumberFracDot:
@@ -270,16 +302,18 @@ func (enc *ReEncoder) handleRune(c rune, t RuneType) error {
rune(enc.uhex[3])<<0
return enc.emit(writeStringChar(enc.Out, c, BackslashEscapeUnicode, enc.BackslashEscape))
- case RuneTypeEOF: // start of next top-level value
- if !enc.ForceTrailingNewlines && (enc.Compact || enc.Indent == "") && !enc.lastNonSpace.IsNumber() {
- return nil
- }
- return enc.emitByte('\n')
- case RuneTypeError: // .Close()
- if !enc.ForceTrailingNewlines {
+ case RuneTypeError: // EOF explicitly stated by .Close()
+ fallthrough
+ case RuneTypeEOF: // EOF implied by the start of the next top-level value
+ enc.wasNumber = enc.lastNonSpace.IsNumber()
+ switch {
+ case enc.ForceTrailingNewlines:
+ t = RuneTypeError // enc.lastNonSpace : an NL isn't needed (we already printed one)
+ return enc.emitByte('\n')
+ default:
+ t = RuneTypeEOF // enc.lastNonSpace : an NL *might* be needed
return nil
}
- return enc.emitByte('\n')
default:
return enc.emitByte(byte(c))
}