mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-19 20:32:35 +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 (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -36,7 +36,7 @@ func (b *Builder) error() Error {
|
|||
|
||||
func (b *Builder) Error() Error {
|
||||
if len(b.errs) == 1 {
|
||||
return From(b.errs[0])
|
||||
return wrap(b.errs[0])
|
||||
}
|
||||
return b.error()
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (b *Builder) Add(err error) *Builder {
|
|||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
switch err := From(err).(type) {
|
||||
switch err := wrap(err).(type) {
|
||||
case *baseError:
|
||||
b.errs = append(b.errs, err.Err)
|
||||
case *nestedError:
|
||||
|
@ -122,3 +122,9 @@ func (b *Builder) AddRange(errs ...error) *Builder {
|
|||
|
||||
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 (
|
||||
"context"
|
||||
|
@ -6,7 +6,7 @@ import (
|
|||
"io"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/error"
|
||||
. "github.com/yusing/go-proxy/internal/gperr"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
type Error interface {
|
||||
error
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -44,7 +44,7 @@ func TestBaseWithExtra(t *testing.T) {
|
|||
|
||||
func TestBaseUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
wrapped := From(err)
|
||||
wrapped := Wrap(err)
|
||||
|
||||
ExpectError(t, err, errors.Unwrap(wrapped))
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func TestBaseUnwrap(t *testing.T) {
|
|||
func TestNestedUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
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 })
|
||||
ExpectTrue(t, ok)
|
||||
|
@ -64,7 +64,7 @@ func TestNestedUnwrap(t *testing.T) {
|
|||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
from := errors.New("error")
|
||||
err := From(from)
|
||||
err := Wrap(from)
|
||||
ExpectError(t, from, err)
|
||||
|
||||
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 (
|
||||
"errors"
|
||||
|
@ -99,7 +99,7 @@ func makeLines(errs []error, level int) []string {
|
|||
}
|
||||
lines := make([]string, 0, len(errs))
|
||||
for _, err := range errs {
|
||||
switch err := From(err).(type) {
|
||||
switch err := wrap(err).(type) {
|
||||
case *nestedError:
|
||||
if err.Err != nil {
|
||||
lines = append(lines, makeLine(err.Err.Error(), level))
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,6 +1,8 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
@ -19,27 +21,50 @@ func Errorf(format string, args ...any) Error {
|
|||
return &baseError{fmt.Errorf(format, args...)}
|
||||
}
|
||||
|
||||
// Wrap wraps message in front of the error message.
|
||||
func Wrap(err error, message ...string) Error {
|
||||
if len(message) == 0 || message[0] == "" {
|
||||
return From(err)
|
||||
if err == nil {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case *baseError:
|
||||
return err
|
||||
case *nestedError:
|
||||
case Error:
|
||||
return 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 {
|
||||
n := 0
|
||||
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)
|
||||
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