github.com/qiniu/x@v1.11.9/errors/errors.go (about)

     1  /*
     2   Copyright 2022 Qiniu Limited (qiniu.com)
     3  
     4   Licensed under the Apache License, Version 2.0 (the "License");
     5   you may not use this file except in compliance with the License.
     6   You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10   Unless required by applicable law or agreed to in writing, software
    11   distributed under the License is distributed on an "AS IS" BASIS,
    12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   See the License for the specific language governing permissions and
    14   limitations under the License.
    15  */
    16  
    17  // Package errors provides errors stack tracking utilities.
    18  package errors
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"reflect"
    25  	"runtime"
    26  	"strconv"
    27  	"strings"
    28  )
    29  
    30  // --------------------------------------------------------------------
    31  
    32  // New returns an error that formats as the given text.
    33  // Each call to New returns a distinct error value even if the text is identical.
    34  func New(msg string) error {
    35  	return errors.New(msg)
    36  }
    37  
    38  // Err returns the cause error.
    39  func Err(err error) error {
    40  	if e, ok := err.(*Frame); ok {
    41  		return Err(e.Err)
    42  	}
    43  	return err
    44  }
    45  
    46  // Summary returns summary of specified error.
    47  func Summary(err error) string {
    48  	e, ok := err.(interface {
    49  		Summary() string
    50  	})
    51  	if !ok {
    52  		return err.Error()
    53  	}
    54  	return e.Summary()
    55  }
    56  
    57  // --------------------------------------------------------------------
    58  
    59  // NotFound is a generic NotFound error.
    60  type NotFound struct {
    61  	Category string
    62  }
    63  
    64  func (p *NotFound) Error() string {
    65  	return fmt.Sprint(p.Category, " not found")
    66  }
    67  
    68  // IsNotFound unwraps err and checks it is a *NotFound object or not.
    69  func IsNotFound(err error) bool {
    70  	for {
    71  		if e, ok := err.(interface{ Unwrap() error }); ok {
    72  			err = e.Unwrap()
    73  		} else {
    74  			_, ok = err.(*NotFound)
    75  			return ok
    76  		}
    77  	}
    78  }
    79  
    80  // --------------------------------------------------------------------
    81  
    82  type List []error
    83  
    84  func (p *List) Add(err error) {
    85  	*p = append(*p, err)
    86  }
    87  
    88  func (p List) Error() string {
    89  	n := len(p)
    90  	if n >= 2 {
    91  		s := make([]string, n)
    92  		for i, v := range p {
    93  			s[i] = v.Error()
    94  		}
    95  		return strings.Join(s, "\n")
    96  	}
    97  	if n == 1 {
    98  		return p[0].Error()
    99  	}
   100  	return ""
   101  }
   102  
   103  func (p List) Summary() string {
   104  	n := len(p)
   105  	if n >= 2 {
   106  		s := make([]string, n)
   107  		for i, v := range p {
   108  			s[i] = Summary(v)
   109  		}
   110  		return strings.Join(s, "\n")
   111  	}
   112  	if n == 1 {
   113  		return Summary(p[0])
   114  	}
   115  	return ""
   116  }
   117  
   118  func (p List) ToError() error {
   119  	switch len(p) {
   120  	case 1:
   121  		return p[0]
   122  	case 0:
   123  		return nil
   124  	}
   125  	return p
   126  }
   127  
   128  // Format is required by fmt.Formatter
   129  func (p List) Format(s fmt.State, verb rune) {
   130  	switch verb {
   131  	case 'v':
   132  		io.WriteString(s, p.Error())
   133  	case 's':
   134  		io.WriteString(s, p.Summary())
   135  	case 'q':
   136  		fmt.Fprintf(s, "%q", p.Error())
   137  	}
   138  }
   139  
   140  // --------------------------------------------------------------------
   141  
   142  // Frame represents an error frame.
   143  type Frame struct {
   144  	Err  error
   145  	Func string
   146  	Args []interface{}
   147  	Code string
   148  	File string
   149  	Line int
   150  }
   151  
   152  // NewWith creates a new error frame.
   153  func NewWith(err error, code string, n int, fn string, args ...interface{}) *Frame {
   154  	file, line := fileLine()
   155  	return &Frame{Err: err, Func: fn, Args: args, Code: code, File: file, Line: line + n}
   156  }
   157  
   158  func fileLine() (file string, line int) {
   159  	_, file, line, _ = runtime.Caller(2)
   160  	return
   161  }
   162  
   163  // NewFrame creates a new error frame.
   164  func NewFrame(err error, code, file string, line int, fn string, args ...interface{}) *Frame {
   165  	return &Frame{Err: err, Func: fn, Args: args, Code: code, File: file, Line: line}
   166  }
   167  
   168  func (p *Frame) Error() string {
   169  	return string(errorDetail(make([]byte, 0, 32), p))
   170  }
   171  
   172  func (p *Frame) Summary() string {
   173  	return Summary(p.Err)
   174  }
   175  
   176  func errorDetail(b []byte, p *Frame) []byte {
   177  	if f, ok := p.Err.(*Frame); ok {
   178  		b = errorDetail(b, f)
   179  	} else {
   180  		b = append(b, p.Err.Error()...)
   181  		b = append(b, "\n\n===> errors stack:\n"...)
   182  	}
   183  	b = append(b, p.Func...)
   184  	b = append(b, '(')
   185  	b = argsDetail(b, p.Args)
   186  	b = append(b, ")\n\t"...)
   187  	b = append(b, p.File...)
   188  	b = append(b, ':')
   189  	b = strconv.AppendInt(b, int64(p.Line), 10)
   190  	b = append(b, ' ')
   191  	b = append(b, p.Code...)
   192  	b = append(b, '\n')
   193  	return b
   194  }
   195  
   196  func argsDetail(b []byte, args []interface{}) []byte {
   197  	nlast := len(args) - 1
   198  	for i, arg := range args {
   199  		b = appendValue(b, arg)
   200  		if i != nlast {
   201  			b = append(b, ',', ' ')
   202  		}
   203  	}
   204  	return b
   205  }
   206  
   207  func appendValue(b []byte, arg interface{}) []byte {
   208  	if arg == nil {
   209  		return append(b, "nil"...)
   210  	}
   211  	v := reflect.ValueOf(arg)
   212  	kind := v.Kind()
   213  	if kind >= reflect.Bool && kind <= reflect.Complex128 {
   214  		return append(b, fmt.Sprint(arg)...)
   215  	}
   216  	if kind == reflect.String {
   217  		val := arg.(string)
   218  		if len(val) > 32 {
   219  			val = val[:16] + "..." + val[len(val)-16:]
   220  		}
   221  		return strconv.AppendQuote(b, val)
   222  	}
   223  	if kind == reflect.Array {
   224  		return append(b, "Array"...)
   225  	}
   226  	if kind == reflect.Struct {
   227  		return append(b, "Struct"...)
   228  	}
   229  	val := v.Pointer()
   230  	b = append(b, '0', 'x')
   231  	return strconv.AppendInt(b, int64(val), 16)
   232  }
   233  
   234  // Unwrap provides compatibility for Go 1.13 error chains.
   235  func (p *Frame) Unwrap() error {
   236  	return p.Err
   237  }
   238  
   239  // Format is required by fmt.Formatter
   240  func (p *Frame) Format(s fmt.State, verb rune) {
   241  	switch verb {
   242  	case 'v':
   243  		io.WriteString(s, p.Error())
   244  	case 's':
   245  		io.WriteString(s, p.Summary())
   246  	case 'q':
   247  		fmt.Fprintf(s, "%q", p.Error())
   248  	}
   249  }
   250  
   251  // --------------------------------------------------------------------
   252  
   253  // CallDetail print a function call shortly.
   254  func CallDetail(msg []byte, fn interface{}, args ...interface{}) []byte {
   255  	f := runtime.FuncForPC(reflect.ValueOf(fn).Pointer())
   256  	if f != nil {
   257  		msg = append(msg, f.Name()...)
   258  		msg = append(msg, '(')
   259  		msg = argsDetail(msg, args)
   260  		msg = append(msg, ')')
   261  	}
   262  	return msg
   263  }
   264  
   265  // --------------------------------------------------------------------
   266  
   267  // ErrorInfo is provided for backward compatibility
   268  //
   269  // Deprecated: Use Frame instead.
   270  type ErrorInfo = Frame
   271  
   272  // Detail is provided for backward compatibility
   273  func (p *ErrorInfo) Detail(err error) *ErrorInfo {
   274  	p.Code = err.Error()
   275  	return p
   276  }
   277  
   278  // NestedObject is provided for backward compatibility
   279  func (p *ErrorInfo) NestedObject() interface{} {
   280  	return p.Err
   281  }
   282  
   283  // ErrorDetail is provided for backward compatibility
   284  func (p *ErrorInfo) ErrorDetail() string {
   285  	return p.Error()
   286  }
   287  
   288  // AppendErrorDetail is provided for backward compatibility
   289  func (p *ErrorInfo) AppendErrorDetail(b []byte) []byte {
   290  	return errorDetail(b, p)
   291  }
   292  
   293  // SummaryErr is provided for backward compatibility
   294  func (p *ErrorInfo) SummaryErr() error {
   295  	return p.Err
   296  }
   297  
   298  // Info is provided for backward compatibility
   299  //
   300  // Deprecated: Use NewWith instead.
   301  func Info(err error, cmd ...interface{}) *ErrorInfo {
   302  	return &Frame{Err: err, Args: cmd}
   303  }
   304  
   305  // InfoEx is provided for backward compatibility
   306  //
   307  // Deprecated: Use NewWith instead.
   308  func InfoEx(calldepth int, err error, cmd ...interface{}) *ErrorInfo {
   309  	return &Frame{Err: err, Args: cmd}
   310  }
   311  
   312  // Detail is provided for backward compatibility
   313  //
   314  // Deprecated: Use err.Error() instead.
   315  func Detail(err error) string {
   316  	return err.Error()
   317  }
   318  
   319  // --------------------------------------------------------------------