mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 20:42:26 -06:00 
			
		
		
		
	
		
			
	
	
		
			188 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			188 lines
		
	
	
	
		
			5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2018 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 xerrors
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"unicode"
							 | 
						||
| 
								 | 
							
									"unicode/utf8"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"golang.org/x/xerrors/internal"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const percentBangString = "%!"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Errorf formats according to a format specifier and returns the string as a
							 | 
						||
| 
								 | 
							
								// value that satisfies error.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The returned error includes the file and line number of the caller when
							 | 
						||
| 
								 | 
							
								// formatted with additional detail enabled. If the last argument is an error
							 | 
						||
| 
								 | 
							
								// the returned error's Format method will return it if the format string ends
							 | 
						||
| 
								 | 
							
								// with ": %s", ": %v", or ": %w". If the last argument is an error and the
							 | 
						||
| 
								 | 
							
								// format string ends with ": %w", the returned error implements an Unwrap
							 | 
						||
| 
								 | 
							
								// method returning it.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// If the format specifier includes a %w verb with an error operand in a
							 | 
						||
| 
								 | 
							
								// position other than at the end, the returned error will still implement an
							 | 
						||
| 
								 | 
							
								// Unwrap method returning the operand, but the error's Format method will not
							 | 
						||
| 
								 | 
							
								// return the wrapped error.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// It is invalid to include more than one %w verb or to supply it with an
							 | 
						||
| 
								 | 
							
								// operand that does not implement the error interface. The %w verb is otherwise
							 | 
						||
| 
								 | 
							
								// a synonym for %v.
							 | 
						||
| 
								 | 
							
								func Errorf(format string, a ...interface{}) error {
							 | 
						||
| 
								 | 
							
									format = formatPlusW(format)
							 | 
						||
| 
								 | 
							
									// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
							 | 
						||
| 
								 | 
							
									wrap := strings.HasSuffix(format, ": %w")
							 | 
						||
| 
								 | 
							
									idx, format2, ok := parsePercentW(format)
							 | 
						||
| 
								 | 
							
									percentWElsewhere := !wrap && idx >= 0
							 | 
						||
| 
								 | 
							
									if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) {
							 | 
						||
| 
								 | 
							
										err := errorAt(a, len(a)-1)
							 | 
						||
| 
								 | 
							
										if err == nil {
							 | 
						||
| 
								 | 
							
											return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										// TODO: this is not entirely correct. The error value could be
							 | 
						||
| 
								 | 
							
										// printed elsewhere in format if it mixes numbered with unnumbered
							 | 
						||
| 
								 | 
							
										// substitutions. With relatively small changes to doPrintf we can
							 | 
						||
| 
								 | 
							
										// have it optionally ignore extra arguments and pass the argument
							 | 
						||
| 
								 | 
							
										// list in its entirety.
							 | 
						||
| 
								 | 
							
										msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
							 | 
						||
| 
								 | 
							
										frame := Frame{}
							 | 
						||
| 
								 | 
							
										if internal.EnableTrace {
							 | 
						||
| 
								 | 
							
											frame = Caller(1)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if wrap {
							 | 
						||
| 
								 | 
							
											return &wrapError{msg, err, frame}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return &noWrapError{msg, err, frame}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// Support %w anywhere.
							 | 
						||
| 
								 | 
							
									// TODO: don't repeat the wrapped error's message when %w occurs in the middle.
							 | 
						||
| 
								 | 
							
									msg := fmt.Sprintf(format2, a...)
							 | 
						||
| 
								 | 
							
									if idx < 0 {
							 | 
						||
| 
								 | 
							
										return &noWrapError{msg, nil, Caller(1)}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									err := errorAt(a, idx)
							 | 
						||
| 
								 | 
							
									if !ok || err == nil {
							 | 
						||
| 
								 | 
							
										// Too many %ws or argument of %w is not an error. Approximate the Go
							 | 
						||
| 
								 | 
							
										// 1.13 fmt.Errorf message.
							 | 
						||
| 
								 | 
							
										return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									frame := Frame{}
							 | 
						||
| 
								 | 
							
									if internal.EnableTrace {
							 | 
						||
| 
								 | 
							
										frame = Caller(1)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return &wrapError{msg, err, frame}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func errorAt(args []interface{}, i int) error {
							 | 
						||
| 
								 | 
							
									if i < 0 || i >= len(args) {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									err, ok := args[i].(error)
							 | 
						||
| 
								 | 
							
									if !ok {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// formatPlusW is used to avoid the vet check that will barf at %w.
							 | 
						||
| 
								 | 
							
								func formatPlusW(s string) string {
							 | 
						||
| 
								 | 
							
									return s
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Return the index of the only %w in format, or -1 if none.
							 | 
						||
| 
								 | 
							
								// Also return a rewritten format string with %w replaced by %v, and
							 | 
						||
| 
								 | 
							
								// false if there is more than one %w.
							 | 
						||
| 
								 | 
							
								// TODO: handle "%[N]w".
							 | 
						||
| 
								 | 
							
								func parsePercentW(format string) (idx int, newFormat string, ok bool) {
							 | 
						||
| 
								 | 
							
									// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
							 | 
						||
| 
								 | 
							
									idx = -1
							 | 
						||
| 
								 | 
							
									ok = true
							 | 
						||
| 
								 | 
							
									n := 0
							 | 
						||
| 
								 | 
							
									sz := 0
							 | 
						||
| 
								 | 
							
									var isW bool
							 | 
						||
| 
								 | 
							
									for i := 0; i < len(format); i += sz {
							 | 
						||
| 
								 | 
							
										if format[i] != '%' {
							 | 
						||
| 
								 | 
							
											sz = 1
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										// "%%" is not a format directive.
							 | 
						||
| 
								 | 
							
										if i+1 < len(format) && format[i+1] == '%' {
							 | 
						||
| 
								 | 
							
											sz = 2
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										sz, isW = parsePrintfVerb(format[i:])
							 | 
						||
| 
								 | 
							
										if isW {
							 | 
						||
| 
								 | 
							
											if idx >= 0 {
							 | 
						||
| 
								 | 
							
												ok = false
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												idx = n
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											// "Replace" the last character, the 'w', with a 'v'.
							 | 
						||
| 
								 | 
							
											p := i + sz - 1
							 | 
						||
| 
								 | 
							
											format = format[:p] + "v" + format[p+1:]
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										n++
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return idx, format, ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Parse the printf verb starting with a % at s[0].
							 | 
						||
| 
								 | 
							
								// Return how many bytes it occupies and whether the verb is 'w'.
							 | 
						||
| 
								 | 
							
								func parsePrintfVerb(s string) (int, bool) {
							 | 
						||
| 
								 | 
							
									// Assume only that the directive is a sequence of non-letters followed by a single letter.
							 | 
						||
| 
								 | 
							
									sz := 0
							 | 
						||
| 
								 | 
							
									var r rune
							 | 
						||
| 
								 | 
							
									for i := 1; i < len(s); i += sz {
							 | 
						||
| 
								 | 
							
										r, sz = utf8.DecodeRuneInString(s[i:])
							 | 
						||
| 
								 | 
							
										if unicode.IsLetter(r) {
							 | 
						||
| 
								 | 
							
											return i + sz, r == 'w'
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return len(s), false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type noWrapError struct {
							 | 
						||
| 
								 | 
							
									msg   string
							 | 
						||
| 
								 | 
							
									err   error
							 | 
						||
| 
								 | 
							
									frame Frame
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *noWrapError) Error() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprint(e)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *noWrapError) FormatError(p Printer) (next error) {
							 | 
						||
| 
								 | 
							
									p.Print(e.msg)
							 | 
						||
| 
								 | 
							
									e.frame.Format(p)
							 | 
						||
| 
								 | 
							
									return e.err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type wrapError struct {
							 | 
						||
| 
								 | 
							
									msg   string
							 | 
						||
| 
								 | 
							
									err   error
							 | 
						||
| 
								 | 
							
									frame Frame
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *wrapError) Error() string {
							 | 
						||
| 
								 | 
							
									return fmt.Sprint(e)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *wrapError) FormatError(p Printer) (next error) {
							 | 
						||
| 
								 | 
							
									p.Print(e.msg)
							 | 
						||
| 
								 | 
							
									e.frame.Format(p)
							 | 
						||
| 
								 | 
							
									return e.err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (e *wrapError) Unwrap() error {
							 | 
						||
| 
								 | 
							
									return e.err
							 | 
						||
| 
								 | 
							
								}
							 |