mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 07:12:26 -05:00 
			
		
		
		
	[chore] update viper version (#2539)
* update viper version * removes our last uses of the slice package * fix tests
This commit is contained in:
		
					parent
					
						
							
								c5eced5fd1
							
						
					
				
			
			
				commit
				
					
						906639ad7e
					
				
			
		
					 166 changed files with 11771 additions and 2782 deletions
				
			
		
							
								
								
									
										577
									
								
								vendor/golang.org/x/exp/slog/handler.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										577
									
								
								vendor/golang.org/x/exp/slog/handler.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,577 @@ | |||
| // 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 ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"golang.org/x/exp/slices" | ||||
| 	"golang.org/x/exp/slog/internal/buffer" | ||||
| ) | ||||
| 
 | ||||
| // A Handler handles log records produced by a Logger.. | ||||
| // | ||||
| // A typical handler may print log records to standard error, | ||||
| // or write them to a file or database, or perhaps augment them | ||||
| // with additional attributes and pass them on to another handler. | ||||
| // | ||||
| // Any of the Handler's methods may be called concurrently with itself | ||||
| // or with other methods. It is the responsibility of the Handler to | ||||
| // manage this concurrency. | ||||
| // | ||||
| // Users of the slog package should not invoke Handler methods directly. | ||||
| // They should use the methods of [Logger] instead. | ||||
| type Handler interface { | ||||
| 	// Enabled reports whether the handler handles records at the given level. | ||||
| 	// The handler ignores records whose level is lower. | ||||
| 	// It is called early, before any arguments are processed, | ||||
| 	// to save effort if the log event should be discarded. | ||||
| 	// If called from a Logger method, the first argument is the context | ||||
| 	// passed to that method, or context.Background() if nil was passed | ||||
| 	// or the method does not take a context. | ||||
| 	// The context is passed so Enabled can use its values | ||||
| 	// to make a decision. | ||||
| 	Enabled(context.Context, Level) bool | ||||
| 
 | ||||
| 	// Handle handles the Record. | ||||
| 	// It will only be called when Enabled returns true. | ||||
| 	// The Context argument is as for Enabled. | ||||
| 	// It is present solely to provide Handlers access to the context's values. | ||||
| 	// Canceling the context should not affect record processing. | ||||
| 	// (Among other things, log messages may be necessary to debug a | ||||
| 	// cancellation-related problem.) | ||||
| 	// | ||||
| 	// Handle methods that produce output should observe the following rules: | ||||
| 	//   - If r.Time is the zero time, ignore the time. | ||||
| 	//   - If r.PC is zero, ignore it. | ||||
| 	//   - Attr's values should be resolved. | ||||
| 	//   - If an Attr's key and value are both the zero value, ignore the Attr. | ||||
| 	//     This can be tested with attr.Equal(Attr{}). | ||||
| 	//   - If a group's key is empty, inline the group's Attrs. | ||||
| 	//   - If a group has no Attrs (even if it has a non-empty key), | ||||
| 	//     ignore it. | ||||
| 	Handle(context.Context, Record) error | ||||
| 
 | ||||
| 	// WithAttrs returns a new Handler whose attributes consist of | ||||
| 	// both the receiver's attributes and the arguments. | ||||
| 	// The Handler owns the slice: it may retain, modify or discard it. | ||||
| 	WithAttrs(attrs []Attr) Handler | ||||
| 
 | ||||
| 	// WithGroup returns a new Handler with the given group appended to | ||||
| 	// the receiver's existing groups. | ||||
| 	// The keys of all subsequent attributes, whether added by With or in a | ||||
| 	// Record, should be qualified by the sequence of group names. | ||||
| 	// | ||||
| 	// How this qualification happens is up to the Handler, so long as | ||||
| 	// this Handler's attribute keys differ from those of another Handler | ||||
| 	// with a different sequence of group names. | ||||
| 	// | ||||
| 	// A Handler should treat WithGroup as starting a Group of Attrs that ends | ||||
| 	// at the end of the log event. That is, | ||||
| 	// | ||||
| 	//     logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2)) | ||||
| 	// | ||||
| 	// should behave like | ||||
| 	// | ||||
| 	//     logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2))) | ||||
| 	// | ||||
| 	// If the name is empty, WithGroup returns the receiver. | ||||
| 	WithGroup(name string) Handler | ||||
| } | ||||
| 
 | ||||
| type defaultHandler struct { | ||||
| 	ch *commonHandler | ||||
| 	// log.Output, except for testing | ||||
| 	output func(calldepth int, message string) error | ||||
| } | ||||
| 
 | ||||
| func newDefaultHandler(output func(int, string) error) *defaultHandler { | ||||
| 	return &defaultHandler{ | ||||
| 		ch:     &commonHandler{json: false}, | ||||
| 		output: output, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (*defaultHandler) Enabled(_ context.Context, l Level) bool { | ||||
| 	return l >= LevelInfo | ||||
| } | ||||
| 
 | ||||
| // Collect the level, attributes and message in a string and | ||||
| // write it with the default log.Logger. | ||||
| // Let the log.Logger handle time and file/line. | ||||
| func (h *defaultHandler) Handle(ctx context.Context, r Record) error { | ||||
| 	buf := buffer.New() | ||||
| 	buf.WriteString(r.Level.String()) | ||||
| 	buf.WriteByte(' ') | ||||
| 	buf.WriteString(r.Message) | ||||
| 	state := h.ch.newHandleState(buf, true, " ", nil) | ||||
| 	defer state.free() | ||||
| 	state.appendNonBuiltIns(r) | ||||
| 
 | ||||
| 	// skip [h.output, defaultHandler.Handle, handlerWriter.Write, log.Output] | ||||
| 	return h.output(4, buf.String()) | ||||
| } | ||||
| 
 | ||||
| func (h *defaultHandler) WithAttrs(as []Attr) Handler { | ||||
| 	return &defaultHandler{h.ch.withAttrs(as), h.output} | ||||
| } | ||||
| 
 | ||||
| func (h *defaultHandler) WithGroup(name string) Handler { | ||||
| 	return &defaultHandler{h.ch.withGroup(name), h.output} | ||||
| } | ||||
| 
 | ||||
| // HandlerOptions are options for a TextHandler or JSONHandler. | ||||
| // A zero HandlerOptions consists entirely of default values. | ||||
| type HandlerOptions struct { | ||||
| 	// AddSource causes the handler to compute the source code position | ||||
| 	// of the log statement and add a SourceKey attribute to the output. | ||||
| 	AddSource bool | ||||
| 
 | ||||
| 	// Level reports the minimum record level that will be logged. | ||||
| 	// The handler discards records with lower levels. | ||||
| 	// If Level is nil, the handler assumes LevelInfo. | ||||
| 	// The handler calls Level.Level for each record processed; | ||||
| 	// to adjust the minimum level dynamically, use a LevelVar. | ||||
| 	Level Leveler | ||||
| 
 | ||||
| 	// ReplaceAttr is called to rewrite each non-group attribute before it is logged. | ||||
| 	// The attribute's value has been resolved (see [Value.Resolve]). | ||||
| 	// If ReplaceAttr returns an Attr with Key == "", the attribute is discarded. | ||||
| 	// | ||||
| 	// The built-in attributes with keys "time", "level", "source", and "msg" | ||||
| 	// are passed to this function, except that time is omitted | ||||
| 	// if zero, and source is omitted if AddSource is false. | ||||
| 	// | ||||
| 	// The first argument is a list of currently open groups that contain the | ||||
| 	// Attr. It must not be retained or modified. ReplaceAttr is never called | ||||
| 	// for Group attributes, only their contents. For example, the attribute | ||||
| 	// list | ||||
| 	// | ||||
| 	//     Int("a", 1), Group("g", Int("b", 2)), Int("c", 3) | ||||
| 	// | ||||
| 	// results in consecutive calls to ReplaceAttr with the following arguments: | ||||
| 	// | ||||
| 	//     nil, Int("a", 1) | ||||
| 	//     []string{"g"}, Int("b", 2) | ||||
| 	//     nil, Int("c", 3) | ||||
| 	// | ||||
| 	// ReplaceAttr can be used to change the default keys of the built-in | ||||
| 	// attributes, convert types (for example, to replace a `time.Time` with the | ||||
| 	// integer seconds since the Unix epoch), sanitize personal information, or | ||||
| 	// remove attributes from the output. | ||||
| 	ReplaceAttr func(groups []string, a Attr) Attr | ||||
| } | ||||
| 
 | ||||
| // Keys for "built-in" attributes. | ||||
| const ( | ||||
| 	// TimeKey is the key used by the built-in handlers for the time | ||||
| 	// when the log method is called. The associated Value is a [time.Time]. | ||||
| 	TimeKey = "time" | ||||
| 	// LevelKey is the key used by the built-in handlers for the level | ||||
| 	// of the log call. The associated value is a [Level]. | ||||
| 	LevelKey = "level" | ||||
| 	// MessageKey is the key used by the built-in handlers for the | ||||
| 	// message of the log call. The associated value is a string. | ||||
| 	MessageKey = "msg" | ||||
| 	// SourceKey is the key used by the built-in handlers for the source file | ||||
| 	// and line of the log call. The associated value is a string. | ||||
| 	SourceKey = "source" | ||||
| ) | ||||
| 
 | ||||
| type commonHandler struct { | ||||
| 	json              bool // true => output JSON; false => output text | ||||
| 	opts              HandlerOptions | ||||
| 	preformattedAttrs []byte | ||||
| 	groupPrefix       string   // for text: prefix of groups opened in preformatting | ||||
| 	groups            []string // all groups started from WithGroup | ||||
| 	nOpenGroups       int      // the number of groups opened in preformattedAttrs | ||||
| 	mu                sync.Mutex | ||||
| 	w                 io.Writer | ||||
| } | ||||
| 
 | ||||
| func (h *commonHandler) clone() *commonHandler { | ||||
| 	// We can't use assignment because we can't copy the mutex. | ||||
| 	return &commonHandler{ | ||||
| 		json:              h.json, | ||||
| 		opts:              h.opts, | ||||
| 		preformattedAttrs: slices.Clip(h.preformattedAttrs), | ||||
| 		groupPrefix:       h.groupPrefix, | ||||
| 		groups:            slices.Clip(h.groups), | ||||
| 		nOpenGroups:       h.nOpenGroups, | ||||
| 		w:                 h.w, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // enabled reports whether l is greater than or equal to the | ||||
| // minimum level. | ||||
| func (h *commonHandler) enabled(l Level) bool { | ||||
| 	minLevel := LevelInfo | ||||
| 	if h.opts.Level != nil { | ||||
| 		minLevel = h.opts.Level.Level() | ||||
| 	} | ||||
| 	return l >= minLevel | ||||
| } | ||||
| 
 | ||||
| func (h *commonHandler) withAttrs(as []Attr) *commonHandler { | ||||
| 	h2 := h.clone() | ||||
| 	// Pre-format the attributes as an optimization. | ||||
| 	prefix := buffer.New() | ||||
| 	defer prefix.Free() | ||||
| 	prefix.WriteString(h.groupPrefix) | ||||
| 	state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "", prefix) | ||||
| 	defer state.free() | ||||
| 	if len(h2.preformattedAttrs) > 0 { | ||||
| 		state.sep = h.attrSep() | ||||
| 	} | ||||
| 	state.openGroups() | ||||
| 	for _, a := range as { | ||||
| 		state.appendAttr(a) | ||||
| 	} | ||||
| 	// Remember the new prefix for later keys. | ||||
| 	h2.groupPrefix = state.prefix.String() | ||||
| 	// Remember how many opened groups are in preformattedAttrs, | ||||
| 	// so we don't open them again when we handle a Record. | ||||
| 	h2.nOpenGroups = len(h2.groups) | ||||
| 	return h2 | ||||
| } | ||||
| 
 | ||||
| func (h *commonHandler) withGroup(name string) *commonHandler { | ||||
| 	if name == "" { | ||||
| 		return h | ||||
| 	} | ||||
| 	h2 := h.clone() | ||||
| 	h2.groups = append(h2.groups, name) | ||||
| 	return h2 | ||||
| } | ||||
| 
 | ||||
| func (h *commonHandler) handle(r Record) error { | ||||
| 	state := h.newHandleState(buffer.New(), true, "", nil) | ||||
| 	defer state.free() | ||||
| 	if h.json { | ||||
| 		state.buf.WriteByte('{') | ||||
| 	} | ||||
| 	// Built-in attributes. They are not in a group. | ||||
| 	stateGroups := state.groups | ||||
| 	state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups. | ||||
| 	rep := h.opts.ReplaceAttr | ||||
| 	// time | ||||
| 	if !r.Time.IsZero() { | ||||
| 		key := TimeKey | ||||
| 		val := r.Time.Round(0) // strip monotonic to match Attr behavior | ||||
| 		if rep == nil { | ||||
| 			state.appendKey(key) | ||||
| 			state.appendTime(val) | ||||
| 		} else { | ||||
| 			state.appendAttr(Time(key, val)) | ||||
| 		} | ||||
| 	} | ||||
| 	// level | ||||
| 	key := LevelKey | ||||
| 	val := r.Level | ||||
| 	if rep == nil { | ||||
| 		state.appendKey(key) | ||||
| 		state.appendString(val.String()) | ||||
| 	} else { | ||||
| 		state.appendAttr(Any(key, val)) | ||||
| 	} | ||||
| 	// source | ||||
| 	if h.opts.AddSource { | ||||
| 		state.appendAttr(Any(SourceKey, r.source())) | ||||
| 	} | ||||
| 	key = MessageKey | ||||
| 	msg := r.Message | ||||
| 	if rep == nil { | ||||
| 		state.appendKey(key) | ||||
| 		state.appendString(msg) | ||||
| 	} else { | ||||
| 		state.appendAttr(String(key, msg)) | ||||
| 	} | ||||
| 	state.groups = stateGroups // Restore groups passed to ReplaceAttrs. | ||||
| 	state.appendNonBuiltIns(r) | ||||
| 	state.buf.WriteByte('\n') | ||||
| 
 | ||||
| 	h.mu.Lock() | ||||
| 	defer h.mu.Unlock() | ||||
| 	_, err := h.w.Write(*state.buf) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendNonBuiltIns(r Record) { | ||||
| 	// preformatted Attrs | ||||
| 	if len(s.h.preformattedAttrs) > 0 { | ||||
| 		s.buf.WriteString(s.sep) | ||||
| 		s.buf.Write(s.h.preformattedAttrs) | ||||
| 		s.sep = s.h.attrSep() | ||||
| 	} | ||||
| 	// Attrs in Record -- unlike the built-in ones, they are in groups started | ||||
| 	// from WithGroup. | ||||
| 	s.prefix = buffer.New() | ||||
| 	defer s.prefix.Free() | ||||
| 	s.prefix.WriteString(s.h.groupPrefix) | ||||
| 	s.openGroups() | ||||
| 	r.Attrs(func(a Attr) bool { | ||||
| 		s.appendAttr(a) | ||||
| 		return true | ||||
| 	}) | ||||
| 	if s.h.json { | ||||
| 		// Close all open groups. | ||||
| 		for range s.h.groups { | ||||
| 			s.buf.WriteByte('}') | ||||
| 		} | ||||
| 		// Close the top-level object. | ||||
| 		s.buf.WriteByte('}') | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // attrSep returns the separator between attributes. | ||||
| func (h *commonHandler) attrSep() string { | ||||
| 	if h.json { | ||||
| 		return "," | ||||
| 	} | ||||
| 	return " " | ||||
| } | ||||
| 
 | ||||
| // handleState holds state for a single call to commonHandler.handle. | ||||
| // The initial value of sep determines whether to emit a separator | ||||
| // before the next key, after which it stays true. | ||||
| type handleState struct { | ||||
| 	h       *commonHandler | ||||
| 	buf     *buffer.Buffer | ||||
| 	freeBuf bool           // should buf be freed? | ||||
| 	sep     string         // separator to write before next key | ||||
| 	prefix  *buffer.Buffer // for text: key prefix | ||||
| 	groups  *[]string      // pool-allocated slice of active groups, for ReplaceAttr | ||||
| } | ||||
| 
 | ||||
| var groupPool = sync.Pool{New: func() any { | ||||
| 	s := make([]string, 0, 10) | ||||
| 	return &s | ||||
| }} | ||||
| 
 | ||||
| func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string, prefix *buffer.Buffer) handleState { | ||||
| 	s := handleState{ | ||||
| 		h:       h, | ||||
| 		buf:     buf, | ||||
| 		freeBuf: freeBuf, | ||||
| 		sep:     sep, | ||||
| 		prefix:  prefix, | ||||
| 	} | ||||
| 	if h.opts.ReplaceAttr != nil { | ||||
| 		s.groups = groupPool.Get().(*[]string) | ||||
| 		*s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) free() { | ||||
| 	if s.freeBuf { | ||||
| 		s.buf.Free() | ||||
| 	} | ||||
| 	if gs := s.groups; gs != nil { | ||||
| 		*gs = (*gs)[:0] | ||||
| 		groupPool.Put(gs) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) openGroups() { | ||||
| 	for _, n := range s.h.groups[s.h.nOpenGroups:] { | ||||
| 		s.openGroup(n) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Separator for group names and keys. | ||||
| const keyComponentSep = '.' | ||||
| 
 | ||||
| // openGroup starts a new group of attributes | ||||
| // with the given name. | ||||
| func (s *handleState) openGroup(name string) { | ||||
| 	if s.h.json { | ||||
| 		s.appendKey(name) | ||||
| 		s.buf.WriteByte('{') | ||||
| 		s.sep = "" | ||||
| 	} else { | ||||
| 		s.prefix.WriteString(name) | ||||
| 		s.prefix.WriteByte(keyComponentSep) | ||||
| 	} | ||||
| 	// Collect group names for ReplaceAttr. | ||||
| 	if s.groups != nil { | ||||
| 		*s.groups = append(*s.groups, name) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // closeGroup ends the group with the given name. | ||||
| func (s *handleState) closeGroup(name string) { | ||||
| 	if s.h.json { | ||||
| 		s.buf.WriteByte('}') | ||||
| 	} else { | ||||
| 		(*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */] | ||||
| 	} | ||||
| 	s.sep = s.h.attrSep() | ||||
| 	if s.groups != nil { | ||||
| 		*s.groups = (*s.groups)[:len(*s.groups)-1] | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // appendAttr appends the Attr's key and value using app. | ||||
| // It handles replacement and checking for an empty key. | ||||
| // after replacement). | ||||
| func (s *handleState) appendAttr(a Attr) { | ||||
| 	if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup { | ||||
| 		var gs []string | ||||
| 		if s.groups != nil { | ||||
| 			gs = *s.groups | ||||
| 		} | ||||
| 		// Resolve before calling ReplaceAttr, so the user doesn't have to. | ||||
| 		a.Value = a.Value.Resolve() | ||||
| 		a = rep(gs, a) | ||||
| 	} | ||||
| 	a.Value = a.Value.Resolve() | ||||
| 	// Elide empty Attrs. | ||||
| 	if a.isEmpty() { | ||||
| 		return | ||||
| 	} | ||||
| 	// Special case: Source. | ||||
| 	if v := a.Value; v.Kind() == KindAny { | ||||
| 		if src, ok := v.Any().(*Source); ok { | ||||
| 			if s.h.json { | ||||
| 				a.Value = src.group() | ||||
| 			} else { | ||||
| 				a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if a.Value.Kind() == KindGroup { | ||||
| 		attrs := a.Value.Group() | ||||
| 		// Output only non-empty groups. | ||||
| 		if len(attrs) > 0 { | ||||
| 			// Inline a group with an empty key. | ||||
| 			if a.Key != "" { | ||||
| 				s.openGroup(a.Key) | ||||
| 			} | ||||
| 			for _, aa := range attrs { | ||||
| 				s.appendAttr(aa) | ||||
| 			} | ||||
| 			if a.Key != "" { | ||||
| 				s.closeGroup(a.Key) | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		s.appendKey(a.Key) | ||||
| 		s.appendValue(a.Value) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendError(err error) { | ||||
| 	s.appendString(fmt.Sprintf("!ERROR:%v", err)) | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendKey(key string) { | ||||
| 	s.buf.WriteString(s.sep) | ||||
| 	if s.prefix != nil { | ||||
| 		// TODO: optimize by avoiding allocation. | ||||
| 		s.appendString(string(*s.prefix) + key) | ||||
| 	} else { | ||||
| 		s.appendString(key) | ||||
| 	} | ||||
| 	if s.h.json { | ||||
| 		s.buf.WriteByte(':') | ||||
| 	} else { | ||||
| 		s.buf.WriteByte('=') | ||||
| 	} | ||||
| 	s.sep = s.h.attrSep() | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendString(str string) { | ||||
| 	if s.h.json { | ||||
| 		s.buf.WriteByte('"') | ||||
| 		*s.buf = appendEscapedJSONString(*s.buf, str) | ||||
| 		s.buf.WriteByte('"') | ||||
| 	} else { | ||||
| 		// text | ||||
| 		if needsQuoting(str) { | ||||
| 			*s.buf = strconv.AppendQuote(*s.buf, str) | ||||
| 		} else { | ||||
| 			s.buf.WriteString(str) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendValue(v Value) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			// If it panics with a nil pointer, the most likely cases are | ||||
| 			// an encoding.TextMarshaler or error fails to guard against nil, | ||||
| 			// in which case "<nil>" seems to be the feasible choice. | ||||
| 			// | ||||
| 			// Adapted from the code in fmt/print.go. | ||||
| 			if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() { | ||||
| 				s.appendString("<nil>") | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			// Otherwise just print the original panic message. | ||||
| 			s.appendString(fmt.Sprintf("!PANIC: %v", r)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	var err error | ||||
| 	if s.h.json { | ||||
| 		err = appendJSONValue(s, v) | ||||
| 	} else { | ||||
| 		err = appendTextValue(s, v) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		s.appendError(err) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *handleState) appendTime(t time.Time) { | ||||
| 	if s.h.json { | ||||
| 		appendJSONTime(s, t) | ||||
| 	} else { | ||||
| 		writeTimeRFC3339Millis(s.buf, t) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // This takes half the time of Time.AppendFormat. | ||||
| func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) { | ||||
| 	year, month, day := t.Date() | ||||
| 	buf.WritePosIntWidth(year, 4) | ||||
| 	buf.WriteByte('-') | ||||
| 	buf.WritePosIntWidth(int(month), 2) | ||||
| 	buf.WriteByte('-') | ||||
| 	buf.WritePosIntWidth(day, 2) | ||||
| 	buf.WriteByte('T') | ||||
| 	hour, min, sec := t.Clock() | ||||
| 	buf.WritePosIntWidth(hour, 2) | ||||
| 	buf.WriteByte(':') | ||||
| 	buf.WritePosIntWidth(min, 2) | ||||
| 	buf.WriteByte(':') | ||||
| 	buf.WritePosIntWidth(sec, 2) | ||||
| 	ns := t.Nanosecond() | ||||
| 	buf.WriteByte('.') | ||||
| 	buf.WritePosIntWidth(ns/1e6, 3) | ||||
| 	_, offsetSeconds := t.Zone() | ||||
| 	if offsetSeconds == 0 { | ||||
| 		buf.WriteByte('Z') | ||||
| 	} else { | ||||
| 		offsetMinutes := offsetSeconds / 60 | ||||
| 		if offsetMinutes < 0 { | ||||
| 			buf.WriteByte('-') | ||||
| 			offsetMinutes = -offsetMinutes | ||||
| 		} else { | ||||
| 			buf.WriteByte('+') | ||||
| 		} | ||||
| 		buf.WritePosIntWidth(offsetMinutes/60, 2) | ||||
| 		buf.WriteByte(':') | ||||
| 		buf.WritePosIntWidth(offsetMinutes%60, 2) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue