// 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 json

import (
	
	
	
	
	
	
	

	
	
	
	
)

// ErrUnknownName indicates that a JSON object member could not be
// unmarshaled because the name is not known to the target Go struct.
// This error is directly wrapped within a [SemanticError] when produced.
//
// The name of an unknown JSON object member can be extracted as:
//
//	err := ...
//	var serr json.SemanticError
//	if errors.As(err, &serr) && serr.Err == json.ErrUnknownName {
//		ptr := serr.JSONPointer // JSON pointer to unknown name
//		name := ptr.LastToken() // unknown name itself
//		...
//	}
//
// This error is only returned if [RejectUnknownMembers] is true.
var ErrUnknownName = errors.New("unknown object member name")

const errorPrefix = "json: "

func isSemanticError( error) bool {
	,  := .(*SemanticError)
	return 
}

func isSyntacticError( error) bool {
	,  := .(*jsontext.SyntacticError)
	return 
}

// isFatalError reports whether this error must terminate asharling.
// All errors are considered fatal unless operating under
// [jsonflags.ReportErrorsWithLegacySemantics] in which case only
// syntactic errors and I/O errors are considered fatal.
func isFatalError( error,  jsonflags.Flags) bool {
	return !.Get(jsonflags.ReportErrorsWithLegacySemantics) ||
		isSyntacticError() || export.IsIOError()
}

// SemanticError describes an error determining the meaning
// of JSON data as Go data or vice-versa.
//
// The contents of this error as produced by this package may change over time.
type SemanticError struct {
	requireKeyedLiterals
	nonComparable

	action string // either "marshal" or "unmarshal"

	// 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 jsontext.Pointer

	// JSONKind is the JSON kind that could not be handled.
	JSONKind jsontext.Kind // may be zero if unknown
	// JSONValue is the JSON number or string that could not be unmarshaled.
	// It is not populated during marshaling.
	JSONValue jsontext.Value // may be nil if irrelevant or unknown
	// GoType is the Go type that could not be handled.
	GoType reflect.Type // may be nil if unknown

	// Err is the underlying error.
	Err error // may be nil
}

// coder is implemented by [jsontext.Encoder] or [jsontext.Decoder].
type coder interface{ StackPointer() jsontext.Pointer }

// newInvalidFormatError wraps err in a SemanticError because
// the current type t cannot handle the provided options format.
// This error must be called before producing or consuming the next value.
//
// If [jsonflags.ReportErrorsWithLegacySemantics] is specified,
// then this automatically skips the next value when unmarshaling
// to ensure that the value is fully consumed.
func newInvalidFormatError( coder,  reflect.Type,  *jsonopts.Struct) error {
	 := fmt.Errorf("invalid format flag %q", .Format)
	switch c := .(type) {
	case *jsontext.Encoder:
		 = newMarshalErrorBefore(, , )
	case *jsontext.Decoder:
		 = newUnmarshalErrorBeforeWithSkipping(, , , )
	}
	return 
}

// newMarshalErrorBefore wraps err in a SemanticError assuming that e
// is positioned right before the next token or value, which causes an error.
func newMarshalErrorBefore( *jsontext.Encoder,  reflect.Type,  error) error {
	return &SemanticError{action: "marshal", GoType: , Err: ,
		ByteOffset:  .OutputOffset() + int64(export.Encoder().CountNextDelimWhitespace()),
		JSONPointer: jsontext.Pointer(export.Encoder().AppendStackPointer(nil, +1))}
}

// newUnmarshalErrorBefore wraps err in a SemanticError assuming that d
// is positioned right before the next token or value, which causes an error.
// It does not record the next JSON kind as this error is used to indicate
// the receiving Go value is invalid to unmarshal into (and not a JSON error).
func newUnmarshalErrorBefore( *jsontext.Decoder,  reflect.Type,  error) error {
	return &SemanticError{action: "unmarshal", GoType: , Err: ,
		ByteOffset:  .InputOffset() + int64(export.Decoder().CountNextDelimWhitespace()),
		JSONPointer: jsontext.Pointer(export.Decoder().AppendStackPointer(nil, +1))}
}

// newUnmarshalErrorBeforeWithSkipping is like [newUnmarshalErrorBefore],
// but automatically skips the next value if
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
func newUnmarshalErrorBeforeWithSkipping( *jsontext.Decoder,  *jsonopts.Struct,  reflect.Type,  error) error {
	 = newUnmarshalErrorBefore(, , )
	if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
		if  := export.Decoder().SkipValue();  != nil {
			return 
		}
	}
	return 
}

// newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
// is positioned right after the previous token or value, which caused an error.
func newUnmarshalErrorAfter( *jsontext.Decoder,  reflect.Type,  error) error {
	 := export.Decoder().PreviousTokenOrValue()
	return &SemanticError{action: "unmarshal", GoType: , Err: ,
		ByteOffset:  .InputOffset() - int64(len()),
		JSONPointer: jsontext.Pointer(export.Decoder().AppendStackPointer(nil, -1)),
		JSONKind:    jsontext.Value().Kind()}
}

// newUnmarshalErrorAfter wraps err in a SemanticError assuming that d
// is positioned right after the previous token or value, which caused an error.
// It also stores a copy of the last JSON value if it is a string or number.
func newUnmarshalErrorAfterWithValue( *jsontext.Decoder,  reflect.Type,  error) error {
	 := newUnmarshalErrorAfter(, , ).(*SemanticError)
	if .JSONKind == '"' || .JSONKind == '0' {
		.JSONValue = jsontext.Value(export.Decoder().PreviousTokenOrValue()).Clone()
	}
	return 
}

// newUnmarshalErrorAfterWithSkipping is like [newUnmarshalErrorAfter],
// but automatically skips the remainder of the current value if
// [jsonflags.ReportErrorsWithLegacySemantics] is specified.
func newUnmarshalErrorAfterWithSkipping( *jsontext.Decoder,  *jsonopts.Struct,  reflect.Type,  error) error {
	 = newUnmarshalErrorAfter(, , )
	if .Flags.Get(jsonflags.ReportErrorsWithLegacySemantics) {
		if  := export.Decoder().SkipValueRemainder();  != nil {
			return 
		}
	}
	return 
}

// newSemanticErrorWithPosition wraps err in a SemanticError assuming that
// the error occurred at the provided depth, and length.
// If err is already a SemanticError, then position information is only
// injected if it is currently unpopulated.
//
// If the position is unpopulated, it is ambiguous where the error occurred
// in the user code, whether it was before or after the current position.
// For the byte offset, we assume that the error occurred before the last read
// token or value when decoding, or before the next value when encoding.
// For the JSON pointer, we point to the parent object or array unless
// we can be certain that it happened with an object member.
//
// This is used to annotate errors returned by user-provided
// v2 MarshalJSON or UnmarshalJSON methods or functions.
func newSemanticErrorWithPosition( coder,  reflect.Type,  int,  int64,  error) error {
	,  := .(*SemanticError)
	if  == nil {
		 = &SemanticError{Err: }
	}
	var  int
	var  int64
	var  interface{ ([]byte, int) []byte }
	var  int64
	switch c := .(type) {
	case *jsontext.Encoder:
		 := export.Encoder()
		.action = cmp.Or(.action, "marshal")
		,  = .Tokens.DepthLength()
		 = .OutputOffset() + int64(export.Encoder().CountNextDelimWhitespace())
		 = 
	case *jsontext.Decoder:
		 := export.Decoder()
		.action = cmp.Or(.action, "unmarshal")
		,  = .Tokens.DepthLength()
		 := .PreviousTokenOrValue()
		 = .InputOffset() - int64(len())
		if ( ==  &&  == ) || len() == 0 {
			// If no Read method was called in the user-defined method or
			// if the Peek method was called, then use the offset of the next value.
			 = .InputOffset() + int64(export.Decoder().CountNextDelimWhitespace())
		}
		 = 
	}
	.ByteOffset = cmp.Or(.ByteOffset, )
	if .JSONPointer == "" {
		 := 0 // default to ambiguous positioning
		switch {
		case  ==  && +0 == :
			 = +1
		case  ==  && +1 == :
			 = -1
		}
		.JSONPointer = jsontext.Pointer(.(nil, ))
	}
	.GoType = cmp.Or(.GoType, )
	return 
}

// collapseSemanticErrors collapses double SemanticErrors at the outer levels
// into a single SemanticError by preserving the inner error,
// but prepending the ByteOffset and JSONPointer with the outer error.
//
// For example:
//
//	collapseSemanticErrors(&SemanticError{
//		ByteOffset:  len64(`[0,{"alpha":[0,1,`),
//		JSONPointer: "/1/alpha/2",
//		GoType:      reflect.TypeFor[outerType](),
//		Err: &SemanticError{
//			ByteOffset:  len64(`{"foo":"bar","fizz":[0,`),
//			JSONPointer: "/fizz/1",
//			GoType:      reflect.TypeFor[innerType](),
//			Err:         ...,
//		},
//	})
//
// results in:
//
//	&SemanticError{
//		ByteOffset:  len64(`[0,{"alpha":[0,1,`) + len64(`{"foo":"bar","fizz":[0,`),
//		JSONPointer: "/1/alpha/2" + "/fizz/1",
//		GoType:      reflect.TypeFor[innerType](),
//		Err:         ...,
//	}
//
// This is used to annotate errors returned by user-provided
// v1 MarshalJSON or UnmarshalJSON methods with precise position information
// if they themselves happened to return a SemanticError.
// Since MarshalJSON and UnmarshalJSON are not operating on the root JSON value,
// their positioning must be relative to the nested JSON value
// returned by UnmarshalJSON or passed to MarshalJSON.
// Therefore, we can construct an absolute position by concatenating
// the outer with the inner positions.
//
// Note that we do not use collapseSemanticErrors with user-provided functions
// that take in an [jsontext.Encoder] or [jsontext.Decoder] since they contain
// methods to report position relative to the root JSON value.
// We assume user-constructed errors are correctly precise about position.
func collapseSemanticErrors( error) error {
	if ,  := .(*SemanticError);  {
		if ,  := .Err.(*SemanticError);  {
			.ByteOffset = .ByteOffset + .ByteOffset
			.JSONPointer = .JSONPointer + .JSONPointer
			* = *
		}
	}
	return 
}

// errorModalVerb is a modal verb like "cannot" or "unable to".
//
// Once per process, Hyrum-proof the error message by deliberately
// switching between equivalent renderings of the same error message.
// The randomization is tied to the Hyrum-proofing already applied
// on map iteration in Go.
var errorModalVerb = sync.OnceValue(func() string {
	for  := range map[string]struct{}{"cannot": {}, "unable to": {}} {
		return  // use whichever phrase we get in the first iteration
	}
	return ""
})

func ( *SemanticError) () string {
	var  strings.Builder
	.WriteString(errorPrefix)
	.WriteString(errorModalVerb())

	// Format action.
	var  string
	switch .action {
	case "marshal":
		.WriteString(" marshal")
		 = " from"
	case "unmarshal":
		.WriteString(" unmarshal")
		 = " into"
	default:
		.WriteString(" handle")
		 = " with"
	}

	// Format JSON kind.
	switch .JSONKind {
	case 'n':
		.WriteString(" JSON null")
	case 'f', 't':
		.WriteString(" JSON boolean")
	case '"':
		.WriteString(" JSON string")
	case '0':
		.WriteString(" JSON number")
	case '{', '}':
		.WriteString(" JSON object")
	case '[', ']':
		.WriteString(" JSON array")
	default:
		if .action == "" {
			 = ""
		}
	}
	if len(.JSONValue) > 0 && len(.JSONValue) < 100 {
		.WriteByte(' ')
		.Write(.JSONValue)
	}

	// Format Go type.
	if .GoType != nil {
		 := .GoType.String()
		if len() > 100 {
			// An excessively long type string most likely occurs for
			// an anonymous struct declaration with many fields.
			// Reduce the noise by just printing the kind,
			// and optionally prepending it with the package name
			// if the struct happens to include an unexported field.
			 = .GoType.Kind().String()
			if .GoType.Kind() == reflect.Struct && .GoType.Name() == "" {
				for  := range .GoType.NumField() {
					if  := .GoType.Field().PkgPath;  != "" {
						 = [strings.LastIndexByte(, '/')+len("/"):] + ".struct"
						break
					}
				}
			}
		}
		.WriteString()
		.WriteString(" Go ")
		.WriteString()
	}

	// Special handling for unknown names.
	if .Err == ErrUnknownName {
		.WriteString(": ")
		.WriteString(ErrUnknownName.Error())
		.WriteString(" ")
		.WriteString(strconv.Quote(.JSONPointer.LastToken()))
		if  := .JSONPointer.Parent();  != "" {
			.WriteString(" within ")
			.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(), 100)))
		}
		return .String()
	}

	// Format where.
	// Avoid printing if it overlaps with a wrapped SyntacticError.
	switch ,  := .Err.(*jsontext.SyntacticError); {
	case .JSONPointer != "":
		if  == nil || !.JSONPointer.Contains(.JSONPointer) {
			.WriteString(" within ")
			.WriteString(strconv.Quote(jsonwire.TruncatePointer(string(.JSONPointer), 100)))
		}
	case .ByteOffset > 0:
		if  == nil || !(.ByteOffset <= .ByteOffset) {
			.WriteString(" after offset ")
			.WriteString(strconv.FormatInt(.ByteOffset, 10))
		}
	}

	// Format underlying error.
	if .Err != nil {
		 := .Err.Error()
		if isSyntacticError(.Err) {
			 = strings.TrimPrefix(, "jsontext: ")
		}
		.WriteString(": ")
		.WriteString()
	}

	return .String()
}

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

func newDuplicateNameError( jsontext.Pointer,  []byte,  int64) error {
	if  != nil {
		,  := jsonwire.AppendUnquote(nil, )
		 = .AppendToken(string())
	}
	return &jsontext.SyntacticError{
		ByteOffset:  ,
		JSONPointer: ,
		Err:         jsontext.ErrDuplicateName,
	}
}