// Copyright 2022 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.

package slog

import (
	
	
	
	
	
	
	
	
	
	
	
)

// JSONHandler is a [Handler] that writes Records to an [io.Writer] as
// line-delimited JSON objects.
type JSONHandler struct {
	*commonHandler
}

// NewJSONHandler creates a [JSONHandler] that writes to w,
// using the given options.
// If opts is nil, the default options are used.
func ( io.Writer,  *HandlerOptions) *JSONHandler {
	if  == nil {
		 = &HandlerOptions{}
	}
	return &JSONHandler{
		&commonHandler{
			json: true,
			w:    ,
			opts: *,
			mu:   &sync.Mutex{},
		},
	}
}

// Enabled reports whether the handler handles records at the given level.
// The handler ignores records whose level is lower.
func ( *JSONHandler) ( context.Context,  Level) bool {
	return .commonHandler.enabled()
}

// WithAttrs returns a new [JSONHandler] whose attributes consists
// of h's attributes followed by attrs.
func ( *JSONHandler) ( []Attr) Handler {
	return &JSONHandler{commonHandler: .commonHandler.withAttrs()}
}

func ( *JSONHandler) ( string) Handler {
	return &JSONHandler{commonHandler: .commonHandler.withGroup()}
}

// Handle formats its argument [Record] as a JSON object on a single line.
//
// If the Record's time is zero, the time is omitted.
// Otherwise, the key is "time"
// and the value is output as with json.Marshal.
//
// If the Record's level is zero, the level is omitted.
// Otherwise, the key is "level"
// and the value of [Level.String] is output.
//
// If the AddSource option is set and source information is available,
// the key is "source", and the value is a record of type [Source].
//
// The message's key is "msg".
//
// To modify these or other attributes, or remove them from the output, use
// [HandlerOptions.ReplaceAttr].
//
// Values are formatted as with an [encoding/json.Encoder] with SetEscapeHTML(false),
// with two exceptions.
//
// First, an Attr whose Value is of type error is formatted as a string, by
// calling its Error method. Only errors in Attrs receive this special treatment,
// not errors embedded in structs, slices, maps or other data structures that
// are processed by the [encoding/json] package.
//
// Second, an encoding failure does not cause Handle to return an error.
// Instead, the error message is formatted as a string.
//
// Each call to Handle results in a single serialized call to io.Writer.Write.
func ( *JSONHandler) ( context.Context,  Record) error {
	return .commonHandler.handle()
}

// Adapted from time.Time.MarshalJSON to avoid allocation.
func appendJSONTime( *handleState,  time.Time) {
	if  := .Year();  < 0 ||  >= 10000 {
		// RFC 3339 is clear that years are 4 digits exactly.
		// See golang.org/issue/4556#c15 for more discussion.
		.appendError(errors.New("time.Time year outside of range [0,9999]"))
	}
	.buf.WriteByte('"')
	*.buf = .AppendFormat(*.buf, time.RFC3339Nano)
	.buf.WriteByte('"')
}

func appendJSONValue( *handleState,  Value) error {
	switch .Kind() {
	case KindString:
		.appendString(.str())
	case KindInt64:
		*.buf = strconv.AppendInt(*.buf, .Int64(), 10)
	case KindUint64:
		*.buf = strconv.AppendUint(*.buf, .Uint64(), 10)
	case KindFloat64:
		// json.Marshal is funny about floats; it doesn't
		// always match strconv.AppendFloat. So just call it.
		// That's expensive, but floats are rare.
		if  := appendJSONMarshal(.buf, .Float64());  != nil {
			return 
		}
	case KindBool:
		*.buf = strconv.AppendBool(*.buf, .Bool())
	case KindDuration:
		// Do what json.Marshal does.
		*.buf = strconv.AppendInt(*.buf, int64(.Duration()), 10)
	case KindTime:
		.appendTime(.Time())
	case KindAny:
		 := .Any()
		,  := .(json.Marshaler)
		if ,  := .(error);  && ! {
			.appendString(.Error())
		} else {
			return appendJSONMarshal(.buf, )
		}
	default:
		panic(fmt.Sprintf("bad kind: %s", .Kind()))
	}
	return nil
}

func appendJSONMarshal( *buffer.Buffer,  any) error {
	// Use a json.Encoder to avoid escaping HTML.
	var  bytes.Buffer
	 := json.NewEncoder(&)
	.SetEscapeHTML(false)
	if  := .Encode();  != nil {
		return 
	}
	 := .Bytes()
	.Write([:len()-1]) // remove final newline
	return nil
}

// appendEscapedJSONString escapes s for JSON and appends it to buf.
// It does not surround the string in quotation marks.
//
// Modified from encoding/json/encode.go:encodeState.string,
// with escapeHTML set to false.
func appendEscapedJSONString( []byte,  string) []byte {
	 := func( byte) {  = append(, ) }
	 := func( string) {  = append(, ...) }

	 := 0
	for  := 0;  < len(); {
		if  := [];  < utf8.RuneSelf {
			if safeSet[] {
				++
				continue
			}
			if  <  {
				([:])
			}
			('\\')
			switch  {
			case '\\', '"':
				()
			case '\n':
				('n')
			case '\r':
				('r')
			case '\t':
				('t')
			default:
				// This encodes bytes < 0x20 except for \t, \n and \r.
				(`u00`)
				(hex[>>4])
				(hex[&0xF])
			}
			++
			 = 
			continue
		}
		,  := utf8.DecodeRuneInString([:])
		if  == utf8.RuneError &&  == 1 {
			if  <  {
				([:])
			}
			(`\ufffd`)
			 += 
			 = 
			continue
		}
		// U+2028 is LINE SEPARATOR.
		// U+2029 is PARAGRAPH SEPARATOR.
		// They are both technically valid characters in JSON strings,
		// but don't work in JSONP, which has to be evaluated as JavaScript,
		// and can lead to security holes there. It is valid JSON to
		// escape them, so we do so unconditionally.
		// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
		if  == '\u2028' ||  == '\u2029' {
			if  <  {
				([:])
			}
			(`\u202`)
			(hex[&0xF])
			 += 
			 = 
			continue
		}
		 += 
	}
	if  < len() {
		([:])
	}
	return 
}

const hex = "0123456789abcdef"

// Copied from encoding/json/tables.go.
//
// safeSet holds the value true if the ASCII character with the given array
// position can be represented inside a JSON string without any further
// escaping.
//
// All values are true except for the ASCII control characters (0-31), the
// double quote ("), and the backslash character ("\").
var safeSet = [utf8.RuneSelf]bool{
	' ':      true,
	'!':      true,
	'"':      false,
	'#':      true,
	'$':      true,
	'%':      true,
	'&':      true,
	'\'':     true,
	'(':      true,
	')':      true,
	'*':      true,
	'+':      true,
	',':      true,
	'-':      true,
	'.':      true,
	'/':      true,
	'0':      true,
	'1':      true,
	'2':      true,
	'3':      true,
	'4':      true,
	'5':      true,
	'6':      true,
	'7':      true,
	'8':      true,
	'9':      true,
	':':      true,
	';':      true,
	'<':      true,
	'=':      true,
	'>':      true,
	'?':      true,
	'@':      true,
	'A':      true,
	'B':      true,
	'C':      true,
	'D':      true,
	'E':      true,
	'F':      true,
	'G':      true,
	'H':      true,
	'I':      true,
	'J':      true,
	'K':      true,
	'L':      true,
	'M':      true,
	'N':      true,
	'O':      true,
	'P':      true,
	'Q':      true,
	'R':      true,
	'S':      true,
	'T':      true,
	'U':      true,
	'V':      true,
	'W':      true,
	'X':      true,
	'Y':      true,
	'Z':      true,
	'[':      true,
	'\\':     false,
	']':      true,
	'^':      true,
	'_':      true,
	'`':      true,
	'a':      true,
	'b':      true,
	'c':      true,
	'd':      true,
	'e':      true,
	'f':      true,
	'g':      true,
	'h':      true,
	'i':      true,
	'j':      true,
	'k':      true,
	'l':      true,
	'm':      true,
	'n':      true,
	'o':      true,
	'p':      true,
	'q':      true,
	'r':      true,
	's':      true,
	't':      true,
	'u':      true,
	'v':      true,
	'w':      true,
	'x':      true,
	'y':      true,
	'z':      true,
	'{':      true,
	'|':      true,
	'}':      true,
	'~':      true,
	'\u007f': true,
}