-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
executable file
·108 lines (94 loc) · 3.48 KB
/
errors.go
File metadata and controls
executable file
·108 lines (94 loc) · 3.48 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
package cleango
import (
"errors"
"fmt"
"strings"
)
type ErrorKind int
const (
// System means that something happened that prevents the code continuing.
System ErrorKind = iota
// NotFound indicates the system could not find an expected resource
NotFound
// InvalidInput indicates that the input was expected in some way
InvalidInput
// Duplicate indicates that a resource already exists.
Duplicate
)
// ValidationIssue detail about where an input is wrong. Recommended to use a json path.
type ValidationIssue struct {
Path string
// Message basically anything you want. You can put codes for i18n lookups.
Message string
// Min allows you to specify the lower bound of field.
Min *int `json:"min,omitempty"`
// Max allows you to specify the upper bound of a field.
Max *int `json:"max,omitempty"`
}
// DomainError is the only error definition that is used by domain layer. All repositories and use cases should
// create these errors are necessary. For the most part, use cases probably won't need to interrogate errors.
// Make sure you properly wrap the errors and use errors.Is or errors.As to get the specific details when appropriate.
type DomainError struct {
// Kind which specific error is at hand.
Kind ErrorKind
// Message is a human-readable message describing the cause of the error.
Message string
// UnderlyingCause the source error that caused this. Not to be confused with a wrapped error. This is error is
// optional and to be used, if necessary, to provide an outer layer detailed information that might not need to
// be communicated with the caller.
UnderlyingCause error
// Issues an issues that occurred while validating input. Should be paired with InvalidInput, but it's your
// code base.
Issues []ValidationIssue
}
var toHuman map[ErrorKind]string = make(map[ErrorKind]string)
// InvalidInputKindAsString translates the Kind iota into a human-readable. Update for non-English.
var InvalidInputKindAsString = "invalid input"
// SystemKindAsString translates the Kind iota into human-readable. Update for non-English
var SystemKindAsString = "system"
// NotFoundKindAsString translates the Kind iota into human-readable. Update for non-English
var NotFoundKindAsString = "not found"
func init() {
toHuman[InvalidInput] = InvalidInputKindAsString
toHuman[System] = SystemKindAsString
toHuman[NotFound] = NotFoundKindAsString
}
func (d *DomainError) Error() string {
if d == nil {
return "domain error was nil"
}
kind := toHuman[d.Kind]
errMsg := "[%s - %s]"
all := []any{kind, d.Message}
if d.UnderlyingCause != nil {
all = append(all, d.UnderlyingCause.Error())
errMsg = "[%s - %s (%s)]"
}
return fmt.Sprintf(errMsg, all...)
}
var ToDomainErrorMessage = "converted error"
// ToDomainError will wrap an error. If the error is not a domain error,
// it will create one with the underlying cause set to the original err value.
// This way all errors will unwrap to a DomainError.
//
// If err is nil, return nil. Supports cases where Anwer and Err are set in presenter
// after a simple service invocation.
func ToDomainError(extraMessage string, err error) error {
if err == nil {
return nil
}
var possibleDomainError *DomainError
if errors.As(err, &possibleDomainError) {
if !strings.Contains(extraMessage, "%w") {
// Make sure the error is properly wrapped.
extraMessage += " (%w)"
}
return fmt.Errorf(extraMessage, err)
}
return &DomainError{
Kind: System,
Message: extraMessage,
UnderlyingCause: err,
Issues: nil,
}
}