mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +02:00
refactor: rename module 'err' to 'gperr'
This commit is contained in:
parent
47ab6b8a92
commit
40aa937f54
12 changed files with 262 additions and 71 deletions
|
@ -1,43 +0,0 @@
|
||||||
package err
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"github.com/yusing/go-proxy/internal/logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getLogger(logger ...*zerolog.Logger) *zerolog.Logger {
|
|
||||||
if len(logger) > 0 {
|
|
||||||
return logger[0]
|
|
||||||
}
|
|
||||||
return logging.GetLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogFatal(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Fatal().Msg(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogError(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Error().Msg(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogWarn(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Warn().Msg(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogPanic(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Panic().Msg(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogInfo(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Info().Msg(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:inline
|
|
||||||
func LogDebug(msg string, err error, logger ...*zerolog.Logger) {
|
|
||||||
getLogger(logger...).Debug().Msg(err.Error())
|
|
||||||
}
|
|
106
internal/gperr/README.md
Normal file
106
internal/gperr/README.md
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
# gperr
|
||||||
|
|
||||||
|
gperr is an error interface that supports nested structure and subject highlighting.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### gperr.Error
|
||||||
|
|
||||||
|
The error interface.
|
||||||
|
|
||||||
|
### gperr.New
|
||||||
|
|
||||||
|
Like `errors.New`, but returns a `gperr.Error`.
|
||||||
|
|
||||||
|
### gperr.Wrap
|
||||||
|
|
||||||
|
Like `fmt.Errorf("%s: %w", message, err)`, but returns a `gperr.Error`.
|
||||||
|
|
||||||
|
### gperr.Error.Subject
|
||||||
|
|
||||||
|
Returns a new error with the subject prepended to the error message. The main subject is highlighted.
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := gperr.New("error message")
|
||||||
|
err = err.Subject("bar")
|
||||||
|
err = err.Subject("foo")
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
<code>foo > <span style="color: red;">bar</span>: error message</code>
|
||||||
|
|
||||||
|
### gperr.Error.Subjectf
|
||||||
|
|
||||||
|
Like `gperr.Error.Subject`, but formats the subject with `fmt.Sprintf`.
|
||||||
|
|
||||||
|
### gperr.PrependSubject
|
||||||
|
|
||||||
|
Prepends the subject to the error message like `gperr.Error.Subject`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := gperr.New("error message")
|
||||||
|
err = gperr.PrependSubject(err, "foo")
|
||||||
|
err = gperr.PrependSubject(err, "bar")
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
<code>bar > <span style="color: red;">foo</span>: error message</code>
|
||||||
|
|
||||||
|
### gperr.Error.With
|
||||||
|
|
||||||
|
Adds a new error to the error chain.
|
||||||
|
|
||||||
|
```go
|
||||||
|
err := gperr.New("error message")
|
||||||
|
err = err.With(gperr.New("inner error"))
|
||||||
|
err = err.With(gperr.New("inner error 2").With(gperr.New("inner inner error")))
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
error message:
|
||||||
|
• inner error
|
||||||
|
• inner error 2
|
||||||
|
• inner inner error
|
||||||
|
```
|
||||||
|
|
||||||
|
### gperr.Error.Withf
|
||||||
|
|
||||||
|
Like `gperr.Error.With`, but formats the error with `fmt.Errorf`.
|
||||||
|
|
||||||
|
### gperr.Error.Is
|
||||||
|
|
||||||
|
Returns true if the error is equal to the given error.
|
||||||
|
|
||||||
|
### gperr.Builder
|
||||||
|
|
||||||
|
A builder for `gperr.Error`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
builder := gperr.NewBuilder("foo")
|
||||||
|
builder.Add(gperr.New("error message"))
|
||||||
|
builder.Addf("error message: %s", "foo")
|
||||||
|
builder.AddRange(gperr.New("error message 1"), gperr.New("error message 2"))
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
foo:
|
||||||
|
• error message
|
||||||
|
• error message: foo
|
||||||
|
• error message 1
|
||||||
|
• error message 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### gperr.Builder.Build
|
||||||
|
|
||||||
|
Builds a `gperr.Error` from the builder.
|
||||||
|
|
||||||
|
## When to return gperr.Error
|
||||||
|
|
||||||
|
- When you want to return multiple errors
|
||||||
|
- When the error has a subject
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -36,7 +36,7 @@ func (b *Builder) error() Error {
|
||||||
|
|
||||||
func (b *Builder) Error() Error {
|
func (b *Builder) Error() Error {
|
||||||
if len(b.errs) == 1 {
|
if len(b.errs) == 1 {
|
||||||
return From(b.errs[0])
|
return wrap(b.errs[0])
|
||||||
}
|
}
|
||||||
return b.error()
|
return b.error()
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func (b *Builder) Add(err error) *Builder {
|
||||||
b.Lock()
|
b.Lock()
|
||||||
defer b.Unlock()
|
defer b.Unlock()
|
||||||
|
|
||||||
switch err := From(err).(type) {
|
switch err := wrap(err).(type) {
|
||||||
case *baseError:
|
case *baseError:
|
||||||
b.errs = append(b.errs, err.Err)
|
b.errs = append(b.errs, err.Err)
|
||||||
case *nestedError:
|
case *nestedError:
|
||||||
|
@ -122,3 +122,9 @@ func (b *Builder) AddRange(errs ...error) *Builder {
|
||||||
|
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) ForEach(fn func(error)) {
|
||||||
|
for _, err := range b.errs {
|
||||||
|
fn(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package err_test
|
package gperr_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/go-proxy/internal/error"
|
. "github.com/yusing/go-proxy/internal/gperr"
|
||||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
type Error interface {
|
type Error interface {
|
||||||
error
|
error
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -44,7 +44,7 @@ func TestBaseWithExtra(t *testing.T) {
|
||||||
|
|
||||||
func TestBaseUnwrap(t *testing.T) {
|
func TestBaseUnwrap(t *testing.T) {
|
||||||
err := errors.New("err")
|
err := errors.New("err")
|
||||||
wrapped := From(err)
|
wrapped := Wrap(err)
|
||||||
|
|
||||||
ExpectError(t, err, errors.Unwrap(wrapped))
|
ExpectError(t, err, errors.Unwrap(wrapped))
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ func TestBaseUnwrap(t *testing.T) {
|
||||||
func TestNestedUnwrap(t *testing.T) {
|
func TestNestedUnwrap(t *testing.T) {
|
||||||
err := errors.New("err")
|
err := errors.New("err")
|
||||||
err2 := New("err2")
|
err2 := New("err2")
|
||||||
wrapped := From(err).Subject("foo").With(err2.Subject("bar"))
|
wrapped := Wrap(err).Subject("foo").With(err2.Subject("bar"))
|
||||||
|
|
||||||
unwrapper, ok := wrapped.(interface{ Unwrap() []error })
|
unwrapper, ok := wrapped.(interface{ Unwrap() []error })
|
||||||
ExpectTrue(t, ok)
|
ExpectTrue(t, ok)
|
||||||
|
@ -64,7 +64,7 @@ func TestNestedUnwrap(t *testing.T) {
|
||||||
|
|
||||||
func TestErrorIs(t *testing.T) {
|
func TestErrorIs(t *testing.T) {
|
||||||
from := errors.New("error")
|
from := errors.New("error")
|
||||||
err := From(from)
|
err := Wrap(from)
|
||||||
ExpectError(t, from, err)
|
ExpectError(t, from, err)
|
||||||
|
|
||||||
ExpectTrue(t, err.Is(from))
|
ExpectTrue(t, err.Is(from))
|
40
internal/gperr/log.go
Normal file
40
internal/gperr/log.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package gperr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/yusing/go-proxy/internal/common"
|
||||||
|
"github.com/yusing/go-proxy/internal/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func log(msg string, err error, level zerolog.Level, logger ...*zerolog.Logger) {
|
||||||
|
var l *zerolog.Logger
|
||||||
|
if len(logger) > 0 {
|
||||||
|
l = logger[0]
|
||||||
|
} else {
|
||||||
|
l = logging.GetLogger()
|
||||||
|
}
|
||||||
|
l.WithLevel(level).Msg(msg + ": " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogFatal(msg string, err error, logger ...*zerolog.Logger) {
|
||||||
|
if common.IsDebug {
|
||||||
|
LogPanic(msg, err, logger...)
|
||||||
|
}
|
||||||
|
log(msg, err, zerolog.FatalLevel, logger...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogError(msg string, err error, logger ...*zerolog.Logger) {
|
||||||
|
log(msg, err, zerolog.ErrorLevel, logger...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogWarn(msg string, err error, logger ...*zerolog.Logger) {
|
||||||
|
log(msg, err, zerolog.WarnLevel, logger...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogPanic(msg string, err error, logger ...*zerolog.Logger) {
|
||||||
|
log(msg, err, zerolog.PanicLevel, logger...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogDebug(msg string, err error, logger ...*zerolog.Logger) {
|
||||||
|
log(msg, err, zerolog.DebugLevel, logger...)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -99,7 +99,7 @@ func makeLines(errs []error, level int) []string {
|
||||||
}
|
}
|
||||||
lines := make([]string, 0, len(errs))
|
lines := make([]string, 0, len(errs))
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
switch err := From(err).(type) {
|
switch err := wrap(err).(type) {
|
||||||
case *nestedError:
|
case *nestedError:
|
||||||
if err.Err != nil {
|
if err.Err != nil {
|
||||||
lines = append(lines, makeLine(err.Err.Error(), level))
|
lines = append(lines, makeLine(err.Err.Error(), level))
|
|
@ -1,4 +1,4 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,6 +1,8 @@
|
||||||
package err
|
package gperr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,27 +21,50 @@ func Errorf(format string, args ...any) Error {
|
||||||
return &baseError{fmt.Errorf(format, args...)}
|
return &baseError{fmt.Errorf(format, args...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap wraps message in front of the error message.
|
||||||
func Wrap(err error, message ...string) Error {
|
func Wrap(err error, message ...string) Error {
|
||||||
if len(message) == 0 || message[0] == "" {
|
if err == nil {
|
||||||
return From(err)
|
return nil
|
||||||
}
|
}
|
||||||
return Errorf("%w: %s", err, message[0])
|
if len(message) == 0 || message[0] == "" {
|
||||||
|
return wrap(err)
|
||||||
|
}
|
||||||
|
//nolint:errorlint
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *baseError:
|
||||||
|
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
||||||
|
return err
|
||||||
|
case *nestedError:
|
||||||
|
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &baseError{fmt.Errorf("%s: %w", message[0], err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func From(err error) Error {
|
func wrap(err error) Error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
//nolint:errorlint
|
//nolint:errorlint
|
||||||
switch err := err.(type) {
|
switch err := err.(type) {
|
||||||
case *baseError:
|
case Error:
|
||||||
return err
|
|
||||||
case *nestedError:
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return &baseError{err}
|
return &baseError{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsJSONMarshallable(err error) bool {
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *nestedError, *withSubject:
|
||||||
|
return true
|
||||||
|
case *baseError:
|
||||||
|
return IsJSONMarshallable(err.Err)
|
||||||
|
default:
|
||||||
|
var v json.Marshaler
|
||||||
|
return errors.As(err, &v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Join(errors ...error) Error {
|
func Join(errors ...error) Error {
|
||||||
n := 0
|
n := 0
|
||||||
for _, err := range errors {
|
for _, err := range errors {
|
||||||
|
@ -66,9 +91,3 @@ func Collect[T any, Err error, Arg any, Func func(Arg) (T, Err)](eb *Builder, fn
|
||||||
eb.Add(err)
|
eb.Add(err)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func Collect2[T any, Err error, Arg1 any, Arg2 any, Func func(Arg1, Arg2) (T, Err)](eb *Builder, fn Func, arg1 Arg1, arg2 Arg2) T {
|
|
||||||
result, err := fn(arg1, arg2)
|
|
||||||
eb.Add(err)
|
|
||||||
return result
|
|
||||||
}
|
|
63
internal/gperr/utils_test.go
Normal file
63
internal/gperr/utils_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package gperr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testErr struct{}
|
||||||
|
|
||||||
|
func (e *testErr) Error() string {
|
||||||
|
return "test error"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *testErr) MarshalJSON() ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsJSONMarshallable(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "testErr",
|
||||||
|
err: &testErr{},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "baseError",
|
||||||
|
err: &baseError{},
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "baseError with json marshallable error",
|
||||||
|
err: &baseError{&testErr{}},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nestedError",
|
||||||
|
err: &nestedError{},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "withSubject",
|
||||||
|
err: &withSubject{},
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard error",
|
||||||
|
err: errors.New("test error"),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if got := IsJSONMarshallable(test.err); got != test.want {
|
||||||
|
t.Errorf("IsJSONMarshallable(%v) = %v, want %v", test.err, got, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue