Files
steampipe/pkg/error_helpers/utils.go

160 lines
3.7 KiB
Go

package error_helpers
import (
"context"
"errors"
"fmt"
"os"
"strings"
"golang.org/x/exp/maps"
"github.com/fatih/color"
"github.com/shiena/ansicolor"
"github.com/spf13/viper"
pconstants "github.com/turbot/pipe-fittings/v2/constants"
"github.com/turbot/steampipe/v2/pkg/statushooks"
)
func init() {
color.Output = ansicolor.NewAnsiColorWriter(os.Stderr)
}
func WrapError(err error) error {
if err == nil {
return nil
}
return HandleCancelError(err)
}
func FailOnError(err error) {
if err != nil {
err = HandleCancelError(err)
panic(err)
}
}
func FailOnErrorWithMessage(err error, message string) {
if err != nil {
err = HandleCancelError(err)
panic(fmt.Sprintf("%s: %s", message, err.Error()))
}
}
func ShowError(ctx context.Context, err error) {
if err == nil {
return
}
err = HandleCancelError(err)
statushooks.Done(ctx)
fmt.Fprintf(color.Error, "%s: %v\n", pconstants.ColoredErr, TransformErrorToSteampipe(err))
}
// ShowErrorWithMessage displays the given error nicely with the given message
func ShowErrorWithMessage(ctx context.Context, err error, message string) {
if err == nil {
return
}
err = HandleCancelError(err)
statushooks.Done(ctx)
fmt.Fprintf(color.Error, "%s: %s - %v\n", pconstants.ColoredErr, message, TransformErrorToSteampipe(err))
}
// TransformErrorToSteampipe removes the pq: and rpc error prefixes along
// with all the unnecessary information that comes from the
// drivers and libraries
func TransformErrorToSteampipe(err error) error {
if err == nil {
return err
}
// transform to a context
err = HandleCancelError(err)
errString := strings.TrimSpace(err.Error())
// an error that originated from our database/sql driver (always prefixed with "ERROR:")
if strings.HasPrefix(errString, "ERROR:") {
errString = strings.TrimSpace(strings.TrimPrefix(errString, "ERROR:"))
// if this is an RPC Error while talking with the plugin
if strings.HasPrefix(errString, "rpc error") {
// trim out "rpc error: code = Unknown desc ="
errString = strings.TrimPrefix(errString, "rpc error: code = Unknown desc =")
}
}
return fmt.Errorf("%s", strings.TrimSpace(errString))
}
// HandleCancelError modifies a context.Canceled error into a readable error that can
// be printed on the console
func HandleCancelError(err error) error {
if IsCancelledError(err) {
err = errors.New("execution cancelled")
}
return err
}
func HandleQueryTimeoutError(err error) error {
if errors.Is(err, context.DeadlineExceeded) {
err = fmt.Errorf("query timeout exceeded (%ds)", viper.GetInt(pconstants.ArgDatabaseQueryTimeout))
}
return err
}
func IsCancelledError(err error) bool {
return errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "canceling statement due to user request")
}
func ShowWarning(warning string) {
if len(warning) == 0 {
return
}
fmt.Fprintf(color.Error, "%s: %v\n", pconstants.ColoredWarn, warning)
}
func CombineErrorsWithPrefix(prefix string, errors ...error) error {
if len(errors) == 0 {
return nil
}
if allErrorsNil(errors...) {
return nil
}
if len(errors) == 1 {
if len(prefix) == 0 {
return errors[0]
} else {
return fmt.Errorf("%s - %s", prefix, errors[0].Error())
}
}
combinedErrorString := map[string]struct{}{prefix: {}}
for _, e := range errors {
if e == nil {
continue
}
combinedErrorString[e.Error()] = struct{}{}
}
return fmt.Errorf("%s", strings.Join(maps.Keys(combinedErrorString), "\n\t"))
}
func allErrorsNil(errors ...error) bool {
for _, e := range errors {
if e != nil {
return false
}
}
return true
}
func CombineErrors(errors ...error) error {
return CombineErrorsWithPrefix("", errors...)
}
func PrefixError(err error, prefix string) error {
return fmt.Errorf("%s: %s\n", prefix, TransformErrorToSteampipe(err).Error())
}