// Copyright (C) 2022 Luke Shumaker // // SPDX-License-Identifier: GPL-2.0-or-later package json import ( "bufio" "bytes" "encoding/json" "errors" "io" "strconv" "git.lukeshu.com/go/lowmemjson" ) type ( Number = json.Number Marshaler = json.Marshaler RawMessage = json.RawMessage // low-level decode errors //SyntaxError = lowmemjson.DecodeSyntaxError // expose a field UnmarshalFieldError = json.UnmarshalFieldError UnmarshalTypeError = json.UnmarshalTypeError // lowmemjson.DecodeTypeError // high-level decode errors InvalidUnmarshalError = json.InvalidUnmarshalError // lowmemjson.DecodeArgumentError // marshal errors InvalidUTF8Error = json.InvalidUTF8Error MarshalerError = lowmemjson.EncodeMethodError // expose a field UnsupportedTypeError = json.UnsupportedTypeError UnsupportedValueError = json.UnsupportedValueError ) ///////////////////////////////////////////////////////////////////// func MarshalIndent(v any, prefix, indent string) ([]byte, error) { var buf bytes.Buffer formatter := &lowmemjson.ReEncoder{ Out: &buf, Indent: indent, Prefix: prefix, } err := lowmemjson.Encode(formatter, v) return buf.Bytes(), err } func Marshal(v any) ([]byte, error) { var buf bytes.Buffer formatter := &lowmemjson.ReEncoder{ Out: &buf, Compact: true, } err := lowmemjson.Encode(formatter, v) return buf.Bytes(), err } func HTMLEscape(dst *bytes.Buffer, src []byte) { formatter := &lowmemjson.ReEncoder{ Out: dst, } _, _ = formatter.Write(src) } func Compact(dst *bytes.Buffer, src []byte) error { formatter := &lowmemjson.ReEncoder{ Out: dst, Compact: true, BackslashEscape: lowmemjson.EscapePreserve, } _, err := formatter.Write(src) return err } func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { formatter := &lowmemjson.ReEncoder{ Out: dst, Indent: indent, Prefix: prefix, BackslashEscape: lowmemjson.EscapePreserve, } _, err := formatter.Write(src) if se, ok := err.(*lowmemjson.ReEncodeSyntaxError); ok { err = &SyntaxError{ msg: se.Err.Error(), Offset: se.Offset + 1, } } return err } func Valid(data []byte) bool { formatter := &lowmemjson.ReEncoder{ Out: io.Discard, Compact: true, } _, err := formatter.Write(data) return err == nil } func Unmarshal(data []byte, ptr any) error { return NewDecoder(bytes.NewReader(data)).Decode(ptr) } ///////////////////////////////////////////////////////////////////// type Decoder struct { *lowmemjson.Decoder buf *bufio.Reader } func NewDecoder(r io.Reader) *Decoder { br, ok := r.(*bufio.Reader) if !ok { br = bufio.NewReader(r) } return &Decoder{ Decoder: lowmemjson.NewDecoder(br), buf: br, } } func (dec *Decoder) Decode(ptr any) error { err := dec.Decoder.Decode(ptr) if derr, ok := err.(*lowmemjson.DecodeError); ok { switch terr := derr.Err.(type) { case *lowmemjson.DecodeSyntaxError: err = &SyntaxError{ msg: terr.Err.Error(), Offset: terr.Offset, } case *lowmemjson.DecodeTypeError: if typeErr, ok := terr.Err.(*json.UnmarshalTypeError); ok { err = &UnmarshalTypeError{ Value: typeErr.Value, Type: typeErr.Type, Offset: typeErr.Offset, Struct: derr.FieldParent, Field: derr.FieldName, } } else if _, isArgErr := terr.Err.(*lowmemjson.DecodeArgumentError); terr.Err != nil && !isArgErr && !errors.Is(terr.Err, strconv.ErrSyntax) && !errors.Is(terr.Err, strconv.ErrRange) { err = terr.Err } else { err = &UnmarshalTypeError{ Value: terr.JSONType, Type: terr.GoType, Offset: terr.Offset, Struct: derr.FieldParent, Field: derr.FieldName, } } } } return err } func (dec *Decoder) Buffered() io.Reader { dat, _ := dec.buf.Peek(dec.buf.Buffered()) return bytes.NewReader(dat) } //func (dec *Decoder) Token() (Token, error)