From d92cace9c8f36698ed7c0ccf4f91b73028b7a3de Mon Sep 17 00:00:00 2001 From: Suyono Date: Thu, 23 Feb 2023 14:15:51 +1100 Subject: [PATCH] feat: error wrapper --- .idea/.gitignore | 8 +++ .idea/errors.iml | 9 ++++ .idea/modules.xml | 8 +++ .tool-versions | 1 + errors.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++ errors_test.go | 102 ++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ 7 files changed, 262 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/errors.iml create mode 100644 .idea/modules.xml create mode 100644 .tool-versions create mode 100644 errors.go create mode 100644 errors_test.go create mode 100644 go.mod diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/errors.iml b/.idea/errors.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/errors.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1855764 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..9f117be --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +golang 1.16 diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..da408f9 --- /dev/null +++ b/errors.go @@ -0,0 +1,131 @@ +package errors + +import ( + "errors" + "fmt" + "runtime" +) + +type ErrorWithStatus interface { + error + ExitStatus() int + IsSkipPrintMessage() bool + SetError(err error) ErrorWithStatus + SkipMessage() ErrorWithStatus + SetPrintMessage() ErrorWithStatus + SetExitStatus(status int) ErrorWithStatus +} + +type errorWithStatus struct { + err error + status int + skipMessage bool + frames []frame +} + +type frame struct { + file string + line int + function string +} + +func NeedExit(err error) (ErrorWithStatus, bool) { + if err != nil { + if esw, ok := err.(ErrorWithStatus); ok { + return esw, esw.ExitStatus() != 0 + } + } + return nil, false +} + +func NewErrorWithStatus(err error, status int, skipMessage bool) ErrorWithStatus { + return &errorWithStatus{ + err: err, + status: status, + skipMessage: skipMessage, + } +} + +func NewError() ErrorWithStatus { + return &errorWithStatus{ + err: errors.New("error"), + status: -1, + skipMessage: false, + } +} + +func NewErrorWithFrames(err error) ErrorWithStatus { + pcs := make([]uintptr, 0) + npc := runtime.Callers(0, pcs) + + returnValue := &errorWithStatus{ + err: err, + status: -1, + skipMessage: false, + } + + if npc > 0 { + frs := runtime.CallersFrames(pcs) + more := true + var fr runtime.Frame + for more { + fr, more = frs.Next() + returnValue.frames = append(returnValue.frames, frame{ + file: fr.File, + function: fr.Function, + line: fr.Line, + }) + } + } + + return returnValue +} + +func NewErrorfStatus(status int, str string, v ...interface{}) ErrorWithStatus { + return &errorWithStatus{ + err: fmt.Errorf(str, v...), + status: status, + skipMessage: false, + } +} + +func NewErrorf(str string, v ...interface{}) ErrorWithStatus { + return NewErrorfStatus(-1, str, v...) +} + +func (e *errorWithStatus) Error() string { + var s string + for _, fr := range e.frames { + s += fmt.Sprintf("\n%v", fr) + } + + return e.err.Error() + s +} + +func (e *errorWithStatus) ExitStatus() int { + return e.status +} + +func (e *errorWithStatus) IsSkipPrintMessage() bool { + return e.skipMessage +} + +func (e *errorWithStatus) SetError(err error) ErrorWithStatus { + e.err = err + return e +} + +func (e *errorWithStatus) SkipMessage() ErrorWithStatus { + e.skipMessage = true + return e +} + +func (e *errorWithStatus) SetPrintMessage() ErrorWithStatus { + e.skipMessage = false + return e +} + +func (e *errorWithStatus) SetExitStatus(status int) ErrorWithStatus { + e.status = status + return e +} diff --git a/errors_test.go b/errors_test.go new file mode 100644 index 0000000..26ecd6f --- /dev/null +++ b/errors_test.go @@ -0,0 +1,102 @@ +package errors + +import ( + "errors" + "reflect" + "testing" +) + +func TestManual(t *testing.T) { + t.Logf("default: %v\n", NewError()) + +} + +func TestNeedExit(t *testing.T) { + type args struct { + err error + } + + simpleCaseErr := NewError() + CaseNoExitErr := NewError().SetExitStatus(0) + tests := []struct { + name string + args args + want ErrorWithStatus + want1 bool + }{ + { + name: "simple", + args: args{ + err: simpleCaseErr, + }, + want: simpleCaseErr, + want1: true, + }, + { + name: "nil input", + args: args{ + err: nil, + }, + want: nil, + want1: false, + }, + { + name: "not ErrorWithStatus", + args: args{ + err: errors.New("test"), + }, + want: nil, + want1: false, + }, + { + name: "error but no exit", + args: args{ + err: CaseNoExitErr, + }, + want: CaseNoExitErr, + want1: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1 := NeedExit(tt.args.err) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NeedExit() got = %v, want %v", got, tt.want) + } + if got1 != tt.want1 { + t.Errorf("NeedExit() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestNewErrorWithFrames(t *testing.T) { + type args struct { + err error + } + + simplecase := errors.New("test") + tests := []struct { + name string + args args + want ErrorWithStatus + }{ + // TODO: Add test cases. + { + name: "simple", + args: args{ + err: simplecase, + }, + want: NewErrorWithFrames(simplecase), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewErrorWithFrames(tt.args.err); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewErrorWithFrames() = %v, want %v", got, tt.want) + } else { + t.Log("check", got) + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7b51ecd --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.suyono.dev/go/errors + +go 1.16