✨ Add SettableError
This commit is contained in:
parent
95c9d7282b
commit
c0dc44e28e
3 changed files with 88 additions and 5 deletions
48
errorf.go
48
errorf.go
|
|
@ -1,15 +1,28 @@
|
||||||
package errors
|
package errors
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
// Returns a ResponsableError formatted from the message, with the specified status.
|
// Returns a SettableError formatted from the message, with the specified status.
|
||||||
// Errorf passes the heavy lifting off to fmt.Errorf, and returns a similar error.
|
// Errorf passes the heavy lifting off to fmt.Errorf, and returns a similar error.
|
||||||
// If one error is passed in the format, this will return an UnwrappableError.
|
// If one error is passed in the format, this will return an UnwrappableError.
|
||||||
// If more than one error is passed in the format, this will return an UnwrappableErrors.
|
// If more than one error is passed in the format, this will return an UnwrappableErrors.
|
||||||
func Errorf(status int, format string, parts ...any) ResponsableError {
|
//
|
||||||
|
// The Msg method may also be formatted.
|
||||||
|
//
|
||||||
|
// If you want a different user message, use Msg, such as:
|
||||||
|
//
|
||||||
|
// err := errors.Errorf(http.StatusNotFound, "%w", sqlError).Msg("user not found %d", userId)
|
||||||
|
func Errorf(status int, format string, parts ...any) SettableError {
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return &erf{status, format, ""}
|
||||||
|
}
|
||||||
|
|
||||||
err := fmt.Errorf(format, parts...)
|
err := fmt.Errorf(format, parts...)
|
||||||
msg := err.Error()
|
msg := err.Error()
|
||||||
er := &erf{status, msg}
|
er := &erf{status, msg, ""}
|
||||||
if we, ok := err.(wrappedError); ok {
|
if we, ok := err.(wrappedError); ok {
|
||||||
return &wrapErr{er, we.Unwrap()}
|
return &wrapErr{er, we.Unwrap()}
|
||||||
}
|
}
|
||||||
|
|
@ -20,24 +33,49 @@ func Errorf(status int, format string, parts ...any) ResponsableError {
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ ResponsableError = new(erf)
|
var _ ResponsableError = new(erf)
|
||||||
|
var _ SettableError = new(erf)
|
||||||
|
|
||||||
type erf struct {
|
type erf struct {
|
||||||
stat int
|
stat int
|
||||||
|
err string
|
||||||
msg string
|
msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *erf) GetStatus() int {
|
func (e *erf) GetStatus() int {
|
||||||
|
if e.stat < http.StatusContinue || e.stat >= 600 {
|
||||||
|
e.stat = http.StatusInternalServerError
|
||||||
|
}
|
||||||
return e.stat
|
return e.stat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *erf) Error() string {
|
func (e *erf) Error() string {
|
||||||
return e.msg
|
return e.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *erf) GetMsg() string {
|
func (e *erf) GetMsg() string {
|
||||||
|
if e.msg == "" {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
return e.msg
|
return e.msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *erf) Status(status int) SettableError {
|
||||||
|
// GetStatus already handles invalid values, so we'll just ignore this.
|
||||||
|
if status < http.StatusContinue || status >= 600 {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e.stat = status
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *erf) Msg(msg string, parts ...any) SettableError {
|
||||||
|
e.msg = msg
|
||||||
|
if len(parts) > 0 {
|
||||||
|
e.msg = fmt.Sprintf(msg, parts...)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
var _ UnwrappableError = new(wrapErr)
|
var _ UnwrappableError = new(wrapErr)
|
||||||
|
|
||||||
type wrapErr struct {
|
type wrapErr struct {
|
||||||
|
|
|
||||||
|
|
@ -69,3 +69,36 @@ func (s *ErrorfTestSuite) TestWrapTwo() {
|
||||||
s.Assert().Same(wrappedOne, unwrapped[0])
|
s.Assert().Same(wrappedOne, unwrapped[0])
|
||||||
s.Assert().Same(wrappedTwo, unwrapped[1])
|
s.Assert().Same(wrappedTwo, unwrapped[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ErrorfTestSuite) TestSet() {
|
||||||
|
var err SettableError = Errorf(http.StatusTeapot, "Unable to BREW")
|
||||||
|
s.Assert().NotNil(err)
|
||||||
|
|
||||||
|
err.Status(http.StatusTooEarly).Msg("It's only %dAM", 2)
|
||||||
|
s.Assert().Equal(http.StatusTooEarly, err.GetStatus())
|
||||||
|
s.Assert().Equal("It's only 2AM", err.GetMsg())
|
||||||
|
s.Assert().Equal("Unable to BREW", err.Error())
|
||||||
|
|
||||||
|
err.Msg("I am so great")
|
||||||
|
s.Assert().Equal("I am so great", err.GetMsg())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ErrorfTestSuite) TestGetStatusOutsideRange() {
|
||||||
|
var err ResponsableError = Errorf(5, "Hello")
|
||||||
|
s.Assert().NotNil(err)
|
||||||
|
s.Assert().Equal(http.StatusInternalServerError, err.GetStatus())
|
||||||
|
|
||||||
|
err = Errorf(5, "Hello")
|
||||||
|
s.Assert().NotNil(err)
|
||||||
|
s.Assert().Equal(http.StatusInternalServerError, err.GetStatus())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ErrorfTestSuite) TestSetStatusOutsideRange() {
|
||||||
|
var err SettableError = Errorf(http.StatusPaymentRequired, "Hello")
|
||||||
|
s.Assert().NotNil(err)
|
||||||
|
|
||||||
|
err.Status(10)
|
||||||
|
s.Assert().Equal(http.StatusPaymentRequired, err.GetStatus())
|
||||||
|
err.Status(600)
|
||||||
|
s.Assert().Equal(http.StatusPaymentRequired, err.GetStatus())
|
||||||
|
}
|
||||||
|
|
|
||||||
12
interface.go
12
interface.go
|
|
@ -6,12 +6,24 @@ package errors
|
||||||
// GetStatus should return an appropriate HTTP status.
|
// GetStatus should return an appropriate HTTP status.
|
||||||
// GetMsg should return a message suitable to display to the end user. If the message
|
// GetMsg should return a message suitable to display to the end user. If the message
|
||||||
// returned by Error is safe for the end user, it may simply call that.
|
// returned by Error is safe for the end user, it may simply call that.
|
||||||
|
// GetStatus should not return a value outside of the 100 - 599 range.
|
||||||
type ResponsableError interface {
|
type ResponsableError interface {
|
||||||
Error() string
|
Error() string
|
||||||
GetStatus() int
|
GetStatus() int
|
||||||
GetMsg() string
|
GetMsg() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SettableError is a ResponsableError which can be modified after initial creation.
|
||||||
|
// The Status method can set the status, while the Msg method can set user message.
|
||||||
|
// If any values are passed after the message, it should be passed to fmt.Sprintf for formatting.
|
||||||
|
// Methods are chainable.
|
||||||
|
// SetStatus should not use a value outside of 100-599. It may either ignore such values, or panic.
|
||||||
|
type SettableError interface {
|
||||||
|
ResponsableError
|
||||||
|
Status(int) SettableError
|
||||||
|
Msg(string, ...any) SettableError
|
||||||
|
}
|
||||||
|
|
||||||
// UnwrappableError allows a ResponsableError to wrap another error.
|
// UnwrappableError allows a ResponsableError to wrap another error.
|
||||||
// It may be appropriate for Error() to delegate to the wrapped error.
|
// It may be appropriate for Error() to delegate to the wrapped error.
|
||||||
type UnwrappableError interface {
|
type UnwrappableError interface {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue