|  | @@ -0,0 +1,89 @@
 | 
	
		
			
				|  |  | +package rest
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import (
 | 
	
		
			
				|  |  | +	"net/http"
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Error represents a REST API error.
 | 
	
		
			
				|  |  | +// It can be marshaled to JSON with ease.
 | 
	
		
			
				|  |  | +type Error struct {
 | 
	
		
			
				|  |  | +	StatusCode int                    `json:"statusCode"`     // HTTP status code (200, 404, 500 etc.)
 | 
	
		
			
				|  |  | +	Message    string                 `json:"message"`        // Status message ("OK", "Not found", "Internal server error" etc.)
 | 
	
		
			
				|  |  | +	Data       map[string]interface{} `json:"data,omitempty"` // Optional additional data.
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Error retrieves the message of a REST API Error.
 | 
	
		
			
				|  |  | +// If it has a "error" string attached using WithData or WithError, that message is returned.
 | 
	
		
			
				|  |  | +// Otherwise, the Error's own message is returned.
 | 
	
		
			
				|  |  | +func (e Error) Error() string {
 | 
	
		
			
				|  |  | +	if e.Data != nil && e.Data["error"] != nil {
 | 
	
		
			
				|  |  | +		if value, ok := e.Data["error"].(string); ok {
 | 
	
		
			
				|  |  | +			return value
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return e.Message
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Is determines whether the Error is an instance of the target.
 | 
	
		
			
				|  |  | +// https://pkg.go.dev/errors#Is
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +// This function supports matching both any error and a specific error, based on the status code.
 | 
	
		
			
				|  |  | +// Use rest.Err (an empty error) for the former:
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//	errors.Is(err, rest.Err) // True if any REST API Error
 | 
	
		
			
				|  |  | +//	errors.Is(err, rest.ErrBadRequest) // True if error is REST 400 Bad Request
 | 
	
		
			
				|  |  | +func (e Error) Is(target error) bool {
 | 
	
		
			
				|  |  | +	t, ok := target.(Error)
 | 
	
		
			
				|  |  | +	if !ok {
 | 
	
		
			
				|  |  | +		return false
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if t.StatusCode == 0 {
 | 
	
		
			
				|  |  | +		return true
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return t.StatusCode == e.StatusCode
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// WithData returns a copy of the HTTP error with the given data merged in.
 | 
	
		
			
				|  |  | +func (e Error) WithData(data map[string]interface{}) Error {
 | 
	
		
			
				|  |  | +	if e.Data == nil {
 | 
	
		
			
				|  |  | +		e.Data = map[string]any{}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if data != nil {
 | 
	
		
			
				|  |  | +		for key, value := range data {
 | 
	
		
			
				|  |  | +			e.Data[key] = value
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	return e
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// WithError returns a copy of the HTTP error with the given error's message merged in to its additional data.
 | 
	
		
			
				|  |  | +func (e Error) WithError(err error) Error {
 | 
	
		
			
				|  |  | +	return e.WithData(map[string]interface{}{
 | 
	
		
			
				|  |  | +		"error": err.Error(),
 | 
	
		
			
				|  |  | +	})
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// WithMessage returns a copy of the HTTP error with the given message.
 | 
	
		
			
				|  |  | +func (e Error) WithMessage(message string) Error {
 | 
	
		
			
				|  |  | +	e.Message = message
 | 
	
		
			
				|  |  | +	return e
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// WithValue returns a copy of the HTTP error with a single data value added.
 | 
	
		
			
				|  |  | +func (e Error) WithValue(name string, value any) Error {
 | 
	
		
			
				|  |  | +	return e.WithData(map[string]any{
 | 
	
		
			
				|  |  | +		name: value,
 | 
	
		
			
				|  |  | +	})
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// Write writes the HTTP error to an HTTP response as plain text.
 | 
	
		
			
				|  |  | +// Additional data is omitted.
 | 
	
		
			
				|  |  | +func (e Error) Write(w http.ResponseWriter) {
 | 
	
		
			
				|  |  | +	w.WriteHeader(e.StatusCode)
 | 
	
		
			
				|  |  | +	w.Write([]byte(e.Message))
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// WriteJSON writes the HTTP error to an HTTP response as JSON.
 | 
	
		
			
				|  |  | +func (e Error) WriteJSON(w http.ResponseWriter) error {
 | 
	
		
			
				|  |  | +	return WriteResponseJSON(w, e.StatusCode, e)
 | 
	
		
			
				|  |  | +}
 |