Files
gotenberg/internal/pkg/xerror/xerror.go
Julien Neuhart 7bfbda4490 huge refactoring
2019-07-23 13:57:18 +02:00

153 lines
2.9 KiB
Go

package xerror
import (
"bytes"
"fmt"
"strings"
)
// ErrorCode is machine-readable error code.
type ErrorCode string
const (
// InternalCode is an internal error.
InternalCode ErrorCode = "internal"
// InvalidCode occurs when a validation
// failed.
InvalidCode ErrorCode = "invalid"
// TimeoutCode occurs when something
// timed out.
TimeoutCode ErrorCode = "timeout"
)
// Error defines our standard application
// error.
type Error struct {
code ErrorCode
message string
op string
err error
}
// Error returns the string representation of the error message.
func (e Error) Error() string {
var buf bytes.Buffer
// if wrapping an error, print its Error() message.
// Otherwise print the error code & message.
if e.err != nil {
buf.WriteString(e.err.Error())
} else {
if e.code != "" {
fmt.Fprintf(&buf, "<%s> ", e.code)
}
buf.WriteString(e.message)
}
return buf.String()
}
/*
New returns a xerror.Error.
Should be used for wrapping an error
at the end of a function.
*/
func New(op string, previous error) error {
return &Error{
op: op,
err: previous,
}
}
/*
Invalid returns a xerror.Error.
Should be used when an input
is wrong.
*/
func Invalid(op, message string, previous error) error {
return &Error{
code: InvalidCode,
message: message,
op: op,
err: previous,
}
}
/*
Timeout returns a xerror.Error.
Should be used when a timeout occurs.
*/
func Timeout(op, message string, previous error) error {
return &Error{
code: TimeoutCode,
message: message,
op: op,
err: previous,
}
}
// Code returns the code of the root error, if available.
// Otherwise returns InternalCode.
func Code(err error) ErrorCode {
if err == nil {
return ""
}
e, ok := err.(*Error)
if ok && e.code != "" {
return e.code
}
if ok && e.err != nil {
return Code(e.err)
}
return InternalCode
}
const defaultMessage string = "an internal error has occurred: please contact technical support"
// Message returns the human-readable message of the error, if available.
// Otherwise returns a generic error message.
func Message(err error) string {
if err == nil {
return ""
}
e, ok := err.(*Error)
if ok && e.message != "" {
return e.message
}
if ok && e.err != nil {
return Message(e.err)
}
return defaultMessage
}
// Op returns the logical operation of the error, if available.
// Otherwise returns an empty string.
func Op(err error) string {
if err == nil {
return ""
}
e, ok := err.(*Error)
if !ok {
return ""
}
var buf bytes.Buffer
nestedOp := Op(e.err)
if nestedOp != "" {
// we want to avoid having the same op chained.
if e.op != "" && !strings.Contains(nestedOp, e.op) {
fmt.Fprintf(&buf, "%s: %s", e.op, nestedOp)
} else {
fmt.Fprintf(&buf, "%s", nestedOp)
}
} else if e.op != "" {
fmt.Fprintf(&buf, "%s", e.op)
}
return buf.String()
}
// Compile-time checks to ensure type implements desired interfaces.
var (
_ = error(new(Error))
)