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}