errors.go

 1// Copyright (c) Microsoft Corporation.
 2// Licensed under the MIT license.
 3
 4package errors
 5
 6import (
 7	"errors"
 8	"fmt"
 9	"io"
10	"net/http"
11	"reflect"
12	"strings"
13
14	"github.com/kylelemons/godebug/pretty"
15)
16
17var prettyConf = &pretty.Config{
18	IncludeUnexported: false,
19	SkipZeroFields:    true,
20	TrackCycles:       true,
21	Formatter: map[reflect.Type]interface{}{
22		reflect.TypeOf((*io.Reader)(nil)).Elem(): func(r io.Reader) string {
23			b, err := io.ReadAll(r)
24			if err != nil {
25				return "could not read io.Reader content"
26			}
27			return string(b)
28		},
29	},
30}
31
32type verboser interface {
33	Verbose() string
34}
35
36// Verbose prints the most verbose error that the error message has.
37func Verbose(err error) string {
38	build := strings.Builder{}
39	for {
40		if err == nil {
41			break
42		}
43		if v, ok := err.(verboser); ok {
44			build.WriteString(v.Verbose())
45		} else {
46			build.WriteString(err.Error())
47		}
48		err = errors.Unwrap(err)
49	}
50	return build.String()
51}
52
53// New is equivalent to errors.New().
54func New(text string) error {
55	return errors.New(text)
56}
57
58// CallErr represents an HTTP call error. Has a Verbose() method that allows getting the
59// http.Request and Response objects. Implements error.
60type CallErr struct {
61	Req *http.Request
62	// Resp contains response body
63	Resp *http.Response
64	Err  error
65}
66
67// Errors implements error.Error().
68func (e CallErr) Error() string {
69	return e.Err.Error()
70}
71
72// Verbose prints a versbose error message with the request or response.
73func (e CallErr) Verbose() string {
74	e.Resp.Request = nil // This brings in a bunch of TLS crap we don't need
75	e.Resp.TLS = nil     // Same
76	return fmt.Sprintf("%s:\nRequest:\n%s\nResponse:\n%s", e.Err, prettyConf.Sprint(e.Req), prettyConf.Sprint(e.Resp))
77}
78
79// Is reports whether any error in errors chain matches target.
80func Is(err, target error) bool {
81	return errors.Is(err, target)
82}
83
84// As finds the first error in errors chain that matches target,
85// and if so, sets target to that error value and returns true.
86// Otherwise, it returns false.
87func As(err error, target interface{}) bool {
88	return errors.As(err, target)
89}