Skip to content

Commit

Permalink
Merge pull request #3 from HugoByte/DIVE-361-Refactor-Error-Handling-…
Browse files Browse the repository at this point in the history
…and-Error-Messages

Dive 361 error handling and error messages
  • Loading branch information
shreyasbhat0 authored Nov 30, 2023
2 parents cb78cd4 + 6902c76 commit 49705f5
Show file tree
Hide file tree
Showing 10 changed files with 814 additions and 7 deletions.
2 changes: 1 addition & 1 deletion cli/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
DiveIconNodeAlreadyRunning = "Icon Node Already Running"
DiveLogDirectory = "/logs/"
DiveDitLogFile = "divelog.log"
DiveErorLogFile = "error.log"
DiveErrorLogFile = "error.log"
DiveOutFile = "dive.json"
ServiceFilePath = "services.json"
starlarkScript = `
Expand Down
25 changes: 25 additions & 0 deletions cli/common/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,31 @@ import (
log "github.com/sirupsen/logrus"
)

type Dive struct {
log Logger
spinner Spinner
context Context
}

func InitDive() *Dive {
logger := NewDiveLogger("file.txt", "error.txt")

return &Dive{log: logger}
}

func (d *Dive) Log() Logger {

return d.log
}

func (d *Dive) Spinner() Spinner {
return d.spinner
}

func (d *Dive) Context() Context {
return d.context
}

type DiveContext struct {
Ctx context.Context
KurtosisContext *kurtosis_context.KurtosisContext
Expand Down
299 changes: 299 additions & 0 deletions cli/common/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
package common

import (
"fmt"
"reflect"

"github.com/pkg/errors"
)

type ErrorCode int

const (
ErrorCodeGeneral ErrorCode = iota + 1000
)

const (
UnknownError ErrorCode = ErrorCodeGeneral + iota
InvalidEnclaveNameError
UnsupportedOSError
FileError
InvalidCommandError
InvalidEnclaveContextError
InvalidEnclaveConfigError
)

var (
ErrUnknown = NewBase(UnknownError, "UnknownError")
ErrInvalidEnclaveName = NewBase(InvalidEnclaveNameError, "InvalidEnclaveName")
ErrUnsupportedOS = NewBase(UnsupportedOSError, "UnsupportedOS")
ErrFile = NewBase(FileError, "FileError")
ErrInvalidCommand = NewBase(InvalidCommandError, "InvalidCommand")
ErrInvalidEnclaveContext = NewBase(InvalidEnclaveContextError, "InvalidEnclaveContext")
ErrInvalidEnclaveConfig = NewBase(InvalidEnclaveConfigError, "InvalidEnclaveConfig")
)

func (c ErrorCode) New(msg string) error {
return Errorc(c, msg)
}

func (c ErrorCode) Errorf(f string, args ...interface{}) error {
return Errorcf(c, f, args...)
}

func (c ErrorCode) Wrap(e error, msg string) error {
return WrapCodeToError(e, c, msg)
}

func (c ErrorCode) Wrapf(e error, f string, args ...interface{}) error {
return WrapCodeToErrorf(e, c, f, args...)
}

func (c ErrorCode) Equals(e error) bool {
if e == nil {
return false
}
return CodeOf(e) == c
}

// Represent Error With Code and Message

type baseError struct {
code ErrorCode
msg string
}

func (e *baseError) Error() string {
return e.msg
}

func (e *baseError) ErrorCode() ErrorCode {
return e.code
}

func (e *baseError) Format(f fmt.State, c rune) {
switch c {
case 'v', 's', 'q':
fmt.Fprintf(f, "E%04d:%s", e.code, e.msg)
}
}

func (e *baseError) Equals(err error) bool {
if err == nil {
return false
}
return CodeOf(err) == e.code
}

func NewBase(code ErrorCode, msg string) *baseError {
return &baseError{code, msg}
}

/*
Associate an error code with a standard Go error.
Create a new error with a code and a message
*/

type codedError struct {
code ErrorCode
error
}

func (e *codedError) ErrorCode() ErrorCode {
return e.code
}

func (e *codedError) Unwrap() error {
return e.error
}

func Errorc(code ErrorCode, msg string) error {
return &codedError{
code: code,
error: errors.New(msg),
}
}

func Errorcf(code ErrorCode, f string, args ...interface{}) error {
return &codedError{
code: code,
error: errors.Errorf(f, args...),
}
}

func WithCode(err error, code ErrorCode) error {
if _, ok := CoderOf(err); ok {
return WrapCodeToError(err, code, err.Error())
}
return &codedError{
code: code,
error: err,
}
}

/*
Wrapping Existing Error with Additional Context like an error code and message.
Preserves Original error but adds the addtional Context to error messages
*/

type wrappedError struct {
error
code ErrorCode
origin error
}

func (e *wrappedError) Format(f fmt.State, c rune) {
switch c {
case 'v':
if f.Flag('+') {
fmt.Fprintf(f, "E%04d:%+v", e.code, e.error)
fmt.Fprintf(f, "\nWrapping %+v", e.origin)
return
}
fallthrough
case 'q', 's':
fmt.Fprintf(f, "E%04d:%s", e.code, e.error)
}
}

func (e *wrappedError) Unwrap() error {
return e.origin
}

func (e *wrappedError) ErrorCode() ErrorCode {
return e.code
}

func WrapCodeToError(e error, c ErrorCode, msg string) error {
return &wrappedError{
error: errors.New(msg),
code: c,
origin: e,
}
}

func WrapCodeToErrorf(e error, c ErrorCode, f string, args ...interface{}) error {
return &wrappedError{
error: errors.Errorf(f, args...),
code: c,
origin: e,
}
}

// To Add Messaage to an error without changing the error code.

type messageError struct {
error
origin error
}

func (e *messageError) Format(f fmt.State, c rune) {
switch c {
case 'v':
if f.Flag('+') {
fmt.Fprintf(f, "%+v", e.error)
fmt.Fprintf(f, "\nWrapping %+v", e.origin)
return
}
fallthrough
case 's', 'q':
fmt.Fprintf(f, "%s", e.error)
}
}

func (e *messageError) Unwrap() error {
return e.origin
}

func WrapMessageToError(e error, msg string) error {
return &messageError{
error: errors.New(msg),
origin: e,
}
}

func WrapMessageToErrorf(e error, f string, args ...interface{}) error {
return &messageError{
error: errors.Errorf(f, args...),
origin: e,
}
}

// Extract Code from Custom Error Messages
type ErrorCoder interface {
error
ErrorCode() ErrorCode
}

func CoderOf(e error) (ErrorCoder, bool) {
var coder ErrorCoder
if AsValue(&coder, e) {
return coder, true
}
return nil, false
}

func CodeOf(e error) ErrorCode {
if coder, ok := CoderOf(e); ok {
return coder.ErrorCode()
}
return UnknownError
}

func AsValue(ptr interface{}, err error) bool {
type causer interface {
Cause() error
}

type unwrapper interface {
Unwrap() error
}

value := reflect.ValueOf(ptr)
if value.Kind() != reflect.Ptr {
return false
} else {
value = value.Elem()
}
valueType := value.Type()

for {
errValue := reflect.ValueOf(err)
if errValue.Type().AssignableTo(valueType) {
value.Set(errValue)
return true
}
if cause, ok := err.(causer); ok {
err = cause.Cause()
} else if unwrap, ok := err.(unwrapper); ok {
err = unwrap.Unwrap()
} else {
return false
}
}
}

// Is checks whether err is caused by the target.
func Is(err, target error) bool {
type causer interface {
Cause() error
}

type unwrapper interface {
Unwrap() error
}

for {
if err == target {
return true
}
if cause, ok := err.(causer); ok {
err = cause.Cause()
} else if unwrap, ok := err.(unwrapper); ok {
err = unwrap.Unwrap()
} else {
return false
}
}
}
Loading

0 comments on commit 49705f5

Please sign in to comment.