Files
steampipe/sperr/error.go

155 lines
3.3 KiB
Go

package sperr
import (
"fmt"
"io"
"strings"
)
type Error struct {
stack *stack
cause error
detail string
message string
isRootMessage bool
}
// RootCause will retrieves the underlying root error in the error stack
// RootCause will recursively retrieve
// the topmost error that does not have a cause, which is assumed to be
// the original cause.
func (e *Error) RootCause() error {
if e == nil {
return nil
}
type hasCause interface {
Cause() error
}
if e.cause == nil {
// return self if we don't have a cause
// I was created with New
return e
}
if cause, ok := e.cause.(hasCause); ok {
return cause.Cause()
}
return e.cause
}
// Cause returns the underlying cause of this error. Maybe <nil> if this was created with New
func (e *Error) Cause() error {
if e == nil {
return nil
}
return e.cause
}
// Stack retrieves the stack trace of the absolute underlying sperr.Error
func (e *Error) Stack() StackTrace {
if e == nil {
return nil
}
type hasStack interface {
Stack() StackTrace
}
if cause, ok := e.cause.(hasStack); ok {
return cause.Stack()
}
if e.stack == nil {
return StackTrace{}
}
return e.stack.StackTrace()
}
// Unwrap returns the immediately underlying error
func (e *Error) Unwrap() error {
if e == nil {
return nil
}
return e.cause
}
func (e *Error) Error() string {
if e == nil {
return ""
}
res := []string{}
if len(e.message) > 0 {
res = append(res, e.message)
}
if e.isRootMessage || e.cause == nil {
return e.message
}
if e.cause != nil && len(e.cause.Error()) > 0 {
res = append(res, e.cause.Error())
}
return strings.Join(res, ": ")
}
func (e *Error) Detail() string {
if e == nil {
return ""
}
res := []string{}
if len(e.detail) > 0 {
// if this is available - the underlying error will always be a sperr
res = append(res, fmt.Sprintf("%s :: %s", e.message, e.detail))
}
type hasDetail interface {
Detail() string
}
if e.cause != nil {
if asD, ok := e.cause.(hasDetail); ok {
res = append(res, asD.Detail())
} else {
res = append(res, e.Error(), e.cause.Error())
}
}
return strings.Join(res, "\n|-- ")
}
// All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported:
//
// %s print the error. If the error has a Cause it will be
// printed recursively.
// %v see %s
// %+v detailed format - includes messages and detail.
// %#v Each Frame of the error's StackTrace will be printed in detail.
// %q a double-quoted string safely escaped with Go syntax
//
// TODO: add Details for +
func (e *Error) Format(s fmt.State, verb rune) {
if e == nil {
return
}
switch verb {
case 'v':
io.WriteString(s, e.Error())
printStack := s.Flag('#')
printDetail := printStack || s.Flag('+')
if printDetail || printStack {
io.WriteString(s, "\n")
}
if printDetail {
io.WriteString(s, "\nDetails:\n")
io.WriteString(s, e.Detail())
io.WriteString(s, "\n")
}
if printStack {
io.WriteString(s, "\nStack:")
io.WriteString(s, fmt.Sprintf("%+v", e.Stack()))
io.WriteString(s, "\n")
}
case 's':
io.WriteString(s, e.Error())
case 'q':
// fallback to the standard %q for consistent escaping
fmt.Fprintf(s, "%q", e.Error())
}
}