// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build goexperiment.jsonv2

package jsontext

import (
	
	
	

	
)

const errorPrefix = "jsontext: "

type ioError struct {
	action string // either "read" or "write"
	err    error
}

func ( *ioError) () string {
	return errorPrefix + .action + " error: " + .err.Error()
}
func ( *ioError) () error {
	return .err
}

// SyntacticError is a description of a syntactic error that occurred when
// encoding or decoding JSON according to the grammar.
//
// The contents of this error as produced by this package may change over time.
type SyntacticError struct {
	requireKeyedLiterals
	nonComparable

	// ByteOffset indicates that an error occurred after this byte offset.
	ByteOffset int64
	// JSONPointer indicates that an error occurred within this JSON value
	// as indicated using the JSON Pointer notation (see RFC 6901).
	JSONPointer Pointer

	// Err is the underlying error.
	Err error
}

// wrapSyntacticError wraps an error and annotates it with a precise location
// using the provided [encoderState] or [decoderState].
// If err is an [ioError] or [io.EOF], then it is not wrapped.
//
// It takes a relative offset pos that can be resolved into
// an absolute offset using state.offsetAt.
//
// It takes a where that specify how the JSON pointer is derived.
// If the underlying error is a [pointerSuffixError],
// then the suffix is appended to the derived pointer.
func wrapSyntacticError( interface {
	( int) int64
	( []byte,  int) []byte
},  error, ,  int) error {
	if ,  := .(*ioError);  == io.EOF ||  {
		return 
	}
	 := .()
	 := .(nil, )
	if ,  := .(*pointerSuffixError);  {
		 = .appendPointer()
		 = .error
	}
	if ,  := .(*decoderState);  &&  == errMismatchDelim {
		 := "at start of value"
		if len(.Tokens.Stack) > 0 && .Tokens.Last.Length() > 0 {
			switch {
			case .Tokens.Last.isArray():
				 = "after array element (expecting ',' or ']')"
				 = []byte(Pointer().Parent()) // problem is with parent array
			case .Tokens.Last.isObject():
				 = "after object value (expecting ',' or '}')"
				 = []byte(Pointer().Parent()) // problem is with parent object
			}
		}
		 = jsonwire.NewInvalidCharacterError(.buf[:], )
	}
	return &SyntacticError{ByteOffset: , JSONPointer: Pointer(), Err: }
}

func ( *SyntacticError) () string {
	 := .JSONPointer
	 := .ByteOffset
	 := []byte(errorPrefix)
	if .Err != nil {
		 = append(, .Err.Error()...)
		if .Err == ErrDuplicateName {
			 = strconv.AppendQuote(append(, ' '), .LastToken())
			 = .Parent()
			 = 0 // not useful to print offset for duplicate names
		}
	} else {
		 = append(, "syntactic error"...)
	}
	if  != "" {
		 = strconv.AppendQuote(append(, " within "...), jsonwire.TruncatePointer(string(), 100))
	}
	if  > 0 {
		 = strconv.AppendInt(append(, " after offset "...), , 10)
	}
	return string()
}

func ( *SyntacticError) () error {
	return .Err
}

// pointerSuffixError represents a JSON pointer suffix to be appended
// to [SyntacticError.JSONPointer]. It is an internal error type
// used within this package and does not appear in the public API.
//
// This type is primarily used to annotate errors in Encoder.WriteValue
// and Decoder.ReadValue with precise positions.
// At the time WriteValue or ReadValue is called, a JSON pointer to the
// upcoming value can be constructed using the Encoder/Decoder state.
// However, tracking pointers within values during normal operation
// would incur a performance penalty in the error-free case.
//
// To provide precise error locations without this overhead,
// the error is wrapped with object names or array indices
// as the call stack is popped when an error occurs.
// Since this happens in reverse order, pointerSuffixError holds
// the pointer in reverse and is only later reversed when appending to
// the pointer prefix.
//
// For example, if the encoder is at "/alpha/bravo/charlie"
// and an error occurs in WriteValue at "/xray/yankee/zulu", then
// the final pointer should be "/alpha/bravo/charlie/xray/yankee/zulu".
//
// As pointerSuffixError is populated during the error return path,
// it first contains "/zulu", then "/zulu/yankee",
// and finally "/zulu/yankee/xray".
// These tokens are reversed and concatenated to "/alpha/bravo/charlie"
// to form the full pointer.
type pointerSuffixError struct {
	error

	// reversePointer is a JSON pointer, but with each token in reverse order.
	reversePointer []byte
}

// wrapWithObjectName wraps err with a JSON object name access,
// which must be a valid quoted JSON string.
func wrapWithObjectName( error,  []byte) error {
	,  := .(*pointerSuffixError)
	if  == nil {
		 = &pointerSuffixError{error: }
	}
	 := jsonwire.UnquoteMayCopy(, false)
	.reversePointer = appendEscapePointerName(append(.reversePointer, '/'), )
	return 
}

// wrapWithArrayIndex wraps err with a JSON array index access.
func wrapWithArrayIndex( error,  int64) error {
	,  := .(*pointerSuffixError)
	if  == nil {
		 = &pointerSuffixError{error: }
	}
	.reversePointer = strconv.AppendUint(append(.reversePointer, '/'), uint64(), 10)
	return 
}

// appendPointer appends the path encoded in e to the end of pointer.
func ( *pointerSuffixError) ( []byte) []byte {
	// Copy each token in reversePointer to the end of pointer in reverse order.
	// Double reversal means that the appended suffix is now in forward order.
	,  := .reversePointer, 
	for len() > 0 {
		 := bytes.LastIndexByte(, '/')
		,  = [:], append(, [:]...)
	}
	return 
}