-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy patherrors.go
More file actions
162 lines (138 loc) · 4.16 KB
/
errors.go
File metadata and controls
162 lines (138 loc) · 4.16 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package gobl
import (
"encoding/json"
"errors"
"fmt"
"github.com/invopop/gobl/cbc"
"github.com/invopop/gobl/rules"
"github.com/invopop/gobl/schema"
)
// An Error provides a structure to better be able to make error comparisons.
// The contents can also be serialised as JSON ready to send to a client
// if needed, see [MarshalJSON] method.
type Error struct {
key cbc.Key
cause error // the underlying error
}
var (
// ErrNoDocument is provided when the envelope does not contain a
// document payload.
ErrNoDocument = NewError("no-document")
// ErrValidation is used when a document fails a validation request.
ErrValidation = NewError("validation")
// ErrCalculation wraps around errors that we're generated during a
// call to perform calculations on a document.
ErrCalculation = NewError("calculation")
// ErrMarshal is provided when there has been a problem attempting to encode
// or marshal an object, usually into JSON.
ErrMarshal = NewError("marshal")
// ErrSignature identifies an issue related to signatures.
ErrSignature = NewError("signature")
// ErrDigest identifies an issue related to the digest.
ErrDigest = NewError("digest")
// ErrInternal is a "catch-all" for errors that are not expected.
ErrInternal = NewError("internal")
// ErrUnknownSchema is provided when we attempt to determine the schema for an object
// or from an ID and cannot find a match.
ErrUnknownSchema = NewError("unknown-schema")
// ErrInput is used when the input data is malformed or missing required fields.
ErrInput = NewError("input")
// ErrNotFound is used when a requested resource cannot be found.
ErrNotFound = NewError("not-found")
)
// NewError provides a new error with a code that is meant to provide
// a context.
func NewError(key cbc.Key) *Error {
return &Error{key: key}
}
// wrapError is used to ensure that errors are wrapped around the GOBL standard
// error so they can be output in a consistent manner.
func wrapError(err error) error {
if err == nil {
return nil
}
if _, ok := err.(*Error); ok {
return err // nothing to do
}
if errors.Is(err, schema.ErrUnknownSchema) {
return ErrUnknownSchema
}
switch err.(type) {
case rules.Faults:
return ErrValidation.WithCause(err)
}
return ErrInternal.WithCause(err)
}
// Error provides a string representation of the error.
func (e *Error) Error() string {
if e.cause != nil {
msg := e.cause.Error()
return fmt.Sprintf("%s: %s", e.key, msg)
}
return e.key.String()
}
// WithCause is used to copy and add an underlying error to this one,
// unless the errors is already of type [*Error], in which case it will
// be returned as is.
func (e *Error) WithCause(err error) *Error {
if te, ok := err.(*Error); ok {
return te
}
ne := e.copy()
ne.cause = err
return ne
}
// WithReason returns the error with a specific reason.
func (e *Error) WithReason(msg string, a ...interface{}) *Error {
ne := e.copy()
ne.cause = fmt.Errorf(msg, a...)
return ne
}
// Key provides the error's key.
func (e *Error) Key() cbc.Key {
return e.key
}
// Faults returns the errors that are mapped as rule Faults directly
// so that they can be serialised as a structured response.
func (e *Error) Faults() rules.Faults {
if fe, ok := e.cause.(rules.Faults); ok {
return fe
}
return nil
}
// Message returns a string representation of the underlying error.
func (e *Error) Message() string {
if e.cause == nil || e.Faults() != nil {
return ""
}
return e.cause.Error()
}
func (e *Error) copy() *Error {
ne := new(Error)
*ne = *e
return ne
}
// Is checks to see if the target error matches the current error or
// part of the chain.
func (e *Error) Is(target error) bool {
t, ok := target.(*Error)
if !ok {
return errors.Is(e.cause, target)
}
return e.key == t.key
}
// MarshalJSON converts the Error into a valid JSON, correctly
// handling mashaling of cause objects that might not have a
// valid MarhsalJSON method.
func (e *Error) MarshalJSON() ([]byte, error) {
err := struct {
Key cbc.Key `json:"key"`
Faults rules.Faults `json:"faults,omitempty"`
Message string `json:"message,omitempty"`
}{
Key: e.key,
Faults: e.Faults(),
Message: e.Message(),
}
return json.Marshal(err)
}