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