go-fuego / fuego

Golang Fuego - web framework generating OpenAPI 3 spec from source code
https://go-fuego.github.io/fuego/
MIT License
903 stars 43 forks source link

Add Unwrap to `ErrorWithStatus` interface #225

Open dylanhitt opened 2 hours ago

dylanhitt commented 2 hours ago

The current interface is:

// ErrorWithStatus is an interface that can be implemented by an error to provide
// additional information about the error.
type ErrorWithStatus interface {
    error
    StatusCode() int
}

However in order to supply a user defined error the user has to supply something like this:

type MyError fuego.HTTPError

var _ fuego.ErrorWithStatus = MyError{}

func (e MyError) Error() string { return e.Err.Error() }

func (e MyError) StatusCode() int { return http.StatusBadRequest }

func (e MyError) Unwrap() error { return fuego.HTTPError(e) }

If you remove the Unwrap this will still compile but it will return this:

400 Bad Request: 

Vs

400 Bad Request:  <the err>

Unwrap is requited so errors.As can infer the type which we want to always be HTTPError.


However, this code is somewhat brittle, maybe we look at the overall design of custom errors. It feels bad to say here is the interface we need you to adhere too, but basically they have to implement Unwrap always as: func (e MyError) Unwrap() error { return fuego.HTTPError(e) }. I do kinda like that we want everything to be HTTPError as does enforce users to adhere too: RFC 9457. Maybe we just update the docs and explicitly explain that Unwrap must be func (e MyError) Unwrap() error { return fuego.HTTPError(e) } or something along those lines? 🤷

Looking for overall thoughts.

P.S -- I could just be completely missing how to properly do this 😅

dylanhitt commented 2 hours ago

The following also works if a user needs to a carry additional data with in their error type for some reason

type MyError struct {
    err fuego.HTTPError
}

var _ fuego.ErrorWithStatus = MyError{}

func (e MyError) Error() string { return e.err.Error() }

func (e MyError) StatusCode() int { return http.StatusBadRequest }

func (e MyError) Unwrap() error { return fuego.HTTPError(e.err) }