github.com/night-codes/go-json@v0.9.15/internal/encoder/indent.go (about)

     1  package encoder
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/night-codes/go-json/internal/errors"
     8  )
     9  
    10  func takeIndentSrcRuntimeContext(src []byte) (*RuntimeContext, []byte) {
    11  	ctx := TakeRuntimeContext()
    12  	buf := ctx.Buf[:0]
    13  	buf = append(append(buf, src...), nul)
    14  	ctx.Buf = buf
    15  	return ctx, buf
    16  }
    17  
    18  func Indent(buf *bytes.Buffer, src []byte, prefix, indentStr string) error {
    19  	if len(src) == 0 {
    20  		return errors.ErrUnexpectedEndOfJSON("", 0)
    21  	}
    22  
    23  	srcCtx, srcBuf := takeIndentSrcRuntimeContext(src)
    24  	dstCtx := TakeRuntimeContext()
    25  	dst := dstCtx.Buf[:0]
    26  
    27  	dst, err := indentAndWrite(buf, dst, srcBuf, prefix, indentStr)
    28  	if err != nil {
    29  		ReleaseRuntimeContext(srcCtx)
    30  		ReleaseRuntimeContext(dstCtx)
    31  		return err
    32  	}
    33  	dstCtx.Buf = dst
    34  	ReleaseRuntimeContext(srcCtx)
    35  	ReleaseRuntimeContext(dstCtx)
    36  	return nil
    37  }
    38  
    39  func indentAndWrite(buf *bytes.Buffer, dst []byte, src []byte, prefix, indentStr string) ([]byte, error) {
    40  	dst, err := doIndent(dst, src, prefix, indentStr, false)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	if _, err := buf.Write(dst); err != nil {
    45  		return nil, err
    46  	}
    47  	return dst, nil
    48  }
    49  
    50  func doIndent(dst, src []byte, prefix, indentStr string, escape bool) ([]byte, error) {
    51  	buf, cursor, err := indentValue(dst, src, 0, 0, []byte(prefix), []byte(indentStr), escape)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	if err := validateEndBuf(src, cursor); err != nil {
    56  		return nil, err
    57  	}
    58  	return buf, nil
    59  }
    60  
    61  func indentValue(
    62  	dst []byte,
    63  	src []byte,
    64  	indentNum int,
    65  	cursor int64,
    66  	prefix []byte,
    67  	indentBytes []byte,
    68  	escape bool) ([]byte, int64, error) {
    69  	for {
    70  		switch src[cursor] {
    71  		case ' ', '\t', '\n', '\r':
    72  			cursor++
    73  			continue
    74  		case '{':
    75  			return indentObject(dst, src, indentNum, cursor, prefix, indentBytes, escape)
    76  		case '}':
    77  			return nil, 0, errors.ErrSyntax("unexpected character '}'", cursor)
    78  		case '[':
    79  			return indentArray(dst, src, indentNum, cursor, prefix, indentBytes, escape)
    80  		case ']':
    81  			return nil, 0, errors.ErrSyntax("unexpected character ']'", cursor)
    82  		case '"':
    83  			return compactString(dst, src, cursor, escape)
    84  		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
    85  			return compactNumber(dst, src, cursor)
    86  		case 't':
    87  			return compactTrue(dst, src, cursor)
    88  		case 'f':
    89  			return compactFalse(dst, src, cursor)
    90  		case 'n':
    91  			return compactNull(dst, src, cursor)
    92  		default:
    93  			return nil, 0, errors.ErrSyntax(fmt.Sprintf("unexpected character '%c'", src[cursor]), cursor)
    94  		}
    95  	}
    96  }
    97  
    98  func indentObject(
    99  	dst []byte,
   100  	src []byte,
   101  	indentNum int,
   102  	cursor int64,
   103  	prefix []byte,
   104  	indentBytes []byte,
   105  	escape bool) ([]byte, int64, error) {
   106  	if src[cursor] == '{' {
   107  		dst = append(dst, '{')
   108  	} else {
   109  		return nil, 0, errors.ErrExpected("expected { character for object value", cursor)
   110  	}
   111  	cursor = skipWhiteSpace(src, cursor+1)
   112  	if src[cursor] == '}' {
   113  		dst = append(dst, '}')
   114  		return dst, cursor + 1, nil
   115  	}
   116  	indentNum++
   117  	var err error
   118  	for {
   119  		dst = append(append(dst, '\n'), prefix...)
   120  		for i := 0; i < indentNum; i++ {
   121  			dst = append(dst, indentBytes...)
   122  		}
   123  		cursor = skipWhiteSpace(src, cursor)
   124  		dst, cursor, err = compactString(dst, src, cursor, escape)
   125  		if err != nil {
   126  			return nil, 0, err
   127  		}
   128  		cursor = skipWhiteSpace(src, cursor)
   129  		if src[cursor] != ':' {
   130  			return nil, 0, errors.ErrSyntax(
   131  				fmt.Sprintf("invalid character '%c' after object key", src[cursor]),
   132  				cursor+1,
   133  			)
   134  		}
   135  		dst = append(dst, ':', ' ')
   136  		dst, cursor, err = indentValue(dst, src, indentNum, cursor+1, prefix, indentBytes, escape)
   137  		if err != nil {
   138  			return nil, 0, err
   139  		}
   140  		cursor = skipWhiteSpace(src, cursor)
   141  		switch src[cursor] {
   142  		case '}':
   143  			dst = append(append(dst, '\n'), prefix...)
   144  			for i := 0; i < indentNum-1; i++ {
   145  				dst = append(dst, indentBytes...)
   146  			}
   147  			dst = append(dst, '}')
   148  			cursor++
   149  			return dst, cursor, nil
   150  		case ',':
   151  			dst = append(dst, ',')
   152  		default:
   153  			return nil, 0, errors.ErrSyntax(
   154  				fmt.Sprintf("invalid character '%c' after object key:value pair", src[cursor]),
   155  				cursor+1,
   156  			)
   157  		}
   158  		cursor++
   159  	}
   160  }
   161  
   162  func indentArray(
   163  	dst []byte,
   164  	src []byte,
   165  	indentNum int,
   166  	cursor int64,
   167  	prefix []byte,
   168  	indentBytes []byte,
   169  	escape bool) ([]byte, int64, error) {
   170  	if src[cursor] == '[' {
   171  		dst = append(dst, '[')
   172  	} else {
   173  		return nil, 0, errors.ErrExpected("expected [ character for array value", cursor)
   174  	}
   175  	cursor = skipWhiteSpace(src, cursor+1)
   176  	if src[cursor] == ']' {
   177  		dst = append(dst, ']')
   178  		return dst, cursor + 1, nil
   179  	}
   180  	indentNum++
   181  	var err error
   182  	for {
   183  		dst = append(append(dst, '\n'), prefix...)
   184  		for i := 0; i < indentNum; i++ {
   185  			dst = append(dst, indentBytes...)
   186  		}
   187  		dst, cursor, err = indentValue(dst, src, indentNum, cursor, prefix, indentBytes, escape)
   188  		if err != nil {
   189  			return nil, 0, err
   190  		}
   191  		cursor = skipWhiteSpace(src, cursor)
   192  		switch src[cursor] {
   193  		case ']':
   194  			dst = append(append(dst, '\n'), prefix...)
   195  			for i := 0; i < indentNum-1; i++ {
   196  				dst = append(dst, indentBytes...)
   197  			}
   198  			dst = append(dst, ']')
   199  			cursor++
   200  			return dst, cursor, nil
   201  		case ',':
   202  			dst = append(dst, ',')
   203  		default:
   204  			return nil, 0, errors.ErrSyntax(
   205  				fmt.Sprintf("invalid character '%c' after array value", src[cursor]),
   206  				cursor+1,
   207  			)
   208  		}
   209  		cursor++
   210  	}
   211  }