git.lukeshu.com/go/lowmemjson@v0.3.9-0.20230723050957-72f6d13f6fb2/reencode_indent.go (about)

     1  // Copyright (C) 2022-2023  Luke Shumaker <lukeshu@lukeshu.com>
     2  //
     3  // SPDX-License-Identifier: GPL-2.0-or-later
     4  
     5  package lowmemjson
     6  
     7  import (
     8  	"git.lukeshu.com/go/lowmemjson/internal/jsonparse"
     9  )
    10  
    11  type reEncodeIndent struct {
    12  	out reEncoderModule
    13  
    14  	// String to use to indent.
    15  	//
    16  	// Newlines are emitted *between* top-level values; a newline
    17  	// is not emitted after the *last* top-level value.
    18  	Indent string
    19  
    20  	// String to put before indents.
    21  	Prefix string
    22  
    23  	// state
    24  	lastNonSpace       jsonparse.RuneType
    25  	lastNonSpaceNonEOF jsonparse.RuneType
    26  	curIndent          int
    27  }
    28  
    29  var _ reEncoderModule = (*reEncodeIndent)(nil)
    30  
    31  func (enc *reEncodeIndent) PopWriteBarrier() {
    32  	enc.lastNonSpace = enc.lastNonSpaceNonEOF
    33  	enc.out.PopWriteBarrier()
    34  }
    35  
    36  func (enc *reEncodeIndent) HandleRune(c rune, t jsonparse.RuneType, escape BackslashEscapeMode, stackSize int) error {
    37  	// emit newlines between top-level values
    38  	if enc.lastNonSpace == jsonparse.RuneTypeEOF && t != jsonparse.RuneTypeSpace {
    39  		if err := enc.out.HandleRune('\n', jsonparse.RuneTypeSpace, 0, 0); err != nil {
    40  			return err
    41  		}
    42  	}
    43  
    44  	// indent
    45  	switch t {
    46  	case jsonparse.RuneTypeSpace:
    47  		// let us manage whitespace, don't pass it through
    48  		return nil
    49  	case jsonparse.RuneTypeObjectEnd, jsonparse.RuneTypeArrayEnd:
    50  		enc.curIndent--
    51  		switch enc.lastNonSpace {
    52  		case jsonparse.RuneTypeObjectBeg, jsonparse.RuneTypeArrayBeg:
    53  			// collapse
    54  		default:
    55  			if err := enc.emitNlIndent(stackSize + 1); err != nil {
    56  				return err
    57  			}
    58  		}
    59  	default:
    60  		switch enc.lastNonSpace {
    61  		case jsonparse.RuneTypeObjectBeg, jsonparse.RuneTypeObjectComma, jsonparse.RuneTypeArrayBeg, jsonparse.RuneTypeArrayComma:
    62  			if err := enc.emitNlIndent(stackSize); err != nil {
    63  				return err
    64  			}
    65  		case jsonparse.RuneTypeObjectColon:
    66  			if err := enc.out.HandleRune(' ', jsonparse.RuneTypeSpace, 0, stackSize); err != nil {
    67  				return err
    68  			}
    69  		}
    70  		switch t {
    71  		case jsonparse.RuneTypeObjectBeg, jsonparse.RuneTypeArrayBeg:
    72  			enc.curIndent++
    73  		}
    74  	}
    75  
    76  	if t != jsonparse.RuneTypeSpace {
    77  		enc.lastNonSpace = t
    78  		if t != jsonparse.RuneTypeEOF {
    79  			enc.lastNonSpaceNonEOF = t
    80  		}
    81  	}
    82  	return enc.out.HandleRune(c, t, escape, stackSize)
    83  }
    84  
    85  func (enc *reEncodeIndent) emitNlIndent(stackSize int) error {
    86  	if err := enc.out.HandleRune('\n', jsonparse.RuneTypeSpace, 0, stackSize); err != nil {
    87  		return err
    88  	}
    89  	for _, c := range enc.Prefix {
    90  		if err := enc.out.HandleRune(c, jsonparse.RuneTypeSpace, 0, stackSize); err != nil {
    91  			return err
    92  		}
    93  	}
    94  	for i := 0; i < enc.curIndent; i++ {
    95  		for _, c := range enc.Indent {
    96  			if err := enc.out.HandleRune(c, jsonparse.RuneTypeSpace, 0, stackSize); err != nil {
    97  				return err
    98  			}
    99  		}
   100  	}
   101  	return nil
   102  }