aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Azure/azure-pipeline-go/pipeline/error.go
blob: fd008364d63387b482b7a1de2198dbe530284737 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package pipeline

import (
    "fmt"
    "runtime"
)

type causer interface {
    Cause() error
}

// ErrorNode can be an embedded field in a private error object. This field
// adds Program Counter support and a 'cause' (reference to a preceding error).
// When initializing a error type with this embedded field, initialize the
// ErrorNode field by calling ErrorNode{}.Initialize(cause).
type ErrorNode struct {
    pc    uintptr // Represents a Program Counter that you can get symbols for.
    cause error   // Refers to the preceding error (or nil)
}

// Error returns a string with the PC's symbols or "" if the PC is invalid.
// When defining a new error type, have its Error method call this one passing
// it the string representation of the error.
func (e *ErrorNode) Error(msg string) string {
    s := ""
    if fn := runtime.FuncForPC(e.pc); fn != nil {
        file, line := fn.FileLine(e.pc)
        s = fmt.Sprintf("-> %v, %v:%v\n", fn.Name(), file, line)
    }
    s += msg + "\n\n"
    if e.cause != nil {
        s += e.cause.Error() + "\n"
    }
    return s
}

// Cause returns the error that preceded this error.
func (e *ErrorNode) Cause() error { return e.cause }

// Temporary returns true if the error occurred due to a temporary condition.
func (e ErrorNode) Temporary() bool {
    type temporary interface {
        Temporary() bool
    }

    for err := e.cause; err != nil; {
        if t, ok := err.(temporary); ok {
            return t.Temporary()
        }

        if cause, ok := err.(causer); ok {
            err = cause.Cause()
        } else {
            err = nil
        }
    }
    return false
}

// Timeout returns true if the error occurred due to time expiring.
func (e ErrorNode) Timeout() bool {
    type timeout interface {
        Timeout() bool
    }

    for err := e.cause; err != nil; {
        if t, ok := err.(timeout); ok {
            return t.Timeout()
        }

        if cause, ok := err.(causer); ok {
            err = cause.Cause()
        } else {
            err = nil
        }
    }
    return false
}

// Initialize is used to initialize an embedded ErrorNode field.
// It captures the caller's program counter and saves the cause (preceding error).
// To initialize the field, use "ErrorNode{}.Initialize(cause, 3)". A callersToSkip
// value of 3 is very common; but, depending on your code nesting, you may need
// a different value.
func (ErrorNode) Initialize(cause error, callersToSkip int) ErrorNode {
    // Get the PC of Initialize method's caller.
    pc := [1]uintptr{}
    _ = runtime.Callers(callersToSkip, pc[:])
    return ErrorNode{pc: pc[0], cause: cause}
}

// Cause walks all the preceding errors and return the originating error.
func Cause(err error) error {
    for err != nil {
        cause, ok := err.(causer)
        if !ok {
            break
        }
        err = cause.Cause()
    }
    return err
}

// NewError creates a simple string error (like Error.New). But, this
// error also captures the caller's Program Counter and the preceding error.
func NewError(cause error, msg string) error {
    return &pcError{
        ErrorNode: ErrorNode{}.Initialize(cause, 3),
        msg:       msg,
    }
}

// pcError is a simple string error (like error.New) with an ErrorNode (PC & cause).
type pcError struct {
    ErrorNode
    msg string
}

// Error satisfies the error interface. It shows the error with Program Counter
// symbols and calls Error on the preceding error so you can see the full error chain.
func (e *pcError) Error() string { return e.ErrorNode.Error(e.msg) }