Updated module path and significantly extended the StoneError implementation with new functionality, including metadata handling, error wrapping, JSON serialization, and utilities like `IsStoneError`. Enhanced README with detailed examples, usage guidelines, and a thematic introduction.
104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package stoneerror
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"time"
|
|
)
|
|
|
|
// StoneError represents a structured error type with metadata and nested errors.
|
|
// Code indicates the error code for categorization.
|
|
// Message describes the error in a human-readable format.
|
|
// Time specifies when the error occurred.
|
|
// Metadata provides additional contextual data for the error.
|
|
// Err holds an underlying error causing the StoneError.
|
|
type StoneError struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Time time.Time `json:"time"`
|
|
Metadata map[string]interface{} `json:"metadata"`
|
|
Err error `json:"-"`
|
|
}
|
|
|
|
// Error returns the formatted string representation of the StoneError.
|
|
func (e *StoneError) Error() string {
|
|
var errorStr string
|
|
|
|
errorStr = fmt.Sprintf("[%d] %s", e.Code, e.Message)
|
|
|
|
if len(e.Metadata) > 0 {
|
|
errorStr += "\n Metadata:"
|
|
for k, v := range e.Metadata {
|
|
errorStr += fmt.Sprintf("\n %s: %v", k, v)
|
|
}
|
|
}
|
|
|
|
if e.Err != nil {
|
|
errorStr += fmt.Sprintf("\n Caused by: %v", e.Err)
|
|
|
|
var nestedStoneErr *StoneError
|
|
if errors.As(e.Err, &nestedStoneErr) {
|
|
errorStr += "\n" + indentString(nestedStoneErr.Error(), " ")
|
|
}
|
|
}
|
|
|
|
return errorStr
|
|
}
|
|
|
|
// New creates a new instance of StoneError with the provided code and message.
|
|
// It initializes the time to the current UTC time and sets Metadata as an empty map.
|
|
func New(code int, message string) *StoneError {
|
|
return &StoneError{
|
|
Code: code,
|
|
Message: message,
|
|
Time: time.Now().UTC(),
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// Wrap creates a new StoneError with a code, message, and wrapped error.
|
|
func Wrap(err error, code int, message string) *StoneError {
|
|
return &StoneError{
|
|
Code: code,
|
|
Message: message,
|
|
Time: time.Now().UTC(),
|
|
Err: err,
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
}
|
|
|
|
// Unwrap returns the underlying error wrapped by the StoneError instance.
|
|
func (e *StoneError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// WithMetadata adds a key-value pair to the error's metadata and returns the error.
|
|
func (e *StoneError) WithMetadata(key string, value interface{}) *StoneError {
|
|
if e.Metadata == nil {
|
|
e.Metadata = make(map[string]interface{})
|
|
}
|
|
e.Metadata[key] = value
|
|
return e
|
|
}
|
|
|
|
// ToJSON serializes the StoneError object into a JSON RawMessage.
|
|
func (e *StoneError) ToJSON() json.RawMessage {
|
|
jsonData, _ := json.Marshal(e)
|
|
return jsonData
|
|
}
|
|
|
|
// IsStoneError checks if the given error is of type *StoneError.
|
|
func IsStoneError(err error) bool {
|
|
var stoneError *StoneError
|
|
|
|
as := errors.As(err, &stoneError)
|
|
return as
|
|
}
|
|
|
|
// indentString prepends each line of string s with the given indent string.
|
|
func indentString(s string, indent string) string {
|
|
return indent + regexp.MustCompile("\n").ReplaceAllString(s, "\n"+indent)
|
|
}
|