From b42137701b608a7b99fee566b573a0dc7246a938 Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Date: Sat, 12 Apr 2025 17:24:44 -0600 Subject: [PATCH] Refactor and document StoneError with enhanced features 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. --- README.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++- error.go | 90 +++++++++++++++++++++++++++++++++++++++ go.mod | 2 +- 3 files changed, 212 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b6f7275..04f1d24 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,122 @@ -# stoneerror +# ๐Ÿ”ฅ StoneError - 10 Billion Times Error Handling! -Ultimate Go error toolkit! ๐Ÿ’Ž Standardized errors with custom codes, rich metadata & JSON superpowers. Perfect for APIs & microservices. Part of stone-utils ecosystem. Error handling, evolved! ๐Ÿš€ \ No newline at end of file +The most scientifically precise error library from the Kingdom of Science! + +[![Kingdom of Science Approved](https://img.shields.io/badge/Approved%20By-Kingdom%20of%20Science-blueviolet)]() +[![SO BADASS](https://img.shields.io/badge/Science-SO_BADASS!-blueviolet)]() +[![10 BILLION](https://img.shields.io/badge/10_Billion%25-Reliable-blueviolet)]() + +## ๐Ÿš€ Why StoneError? + +- Dr.Stone-themed error handling that would make Senku proud +- Dramatic error traces worthy of a laboratory explosion +- Scientific metadata attached to every error case +- 10 billion percent more organized than standard errors + +## ๐Ÿ’ฅ Installation + +```bash +go get gitstormr.dev/stone-utils/stoneerror@latest +``` + +## โšก Basic Usage + +```go +package main + +import ( + "errors" + "fmt" + "gitstormr.dev/stone-utils/stoneerror" +) + +func main() { + // Create new scientific error + err := stoneerror.New(4001, "Invalid chemical formula"). + WithMetadata("element", "H2O2"). + WithMetadata("expected", "H2O") + + // Wrap existing errors with scientific precision + dbErr := errors.New("connection timeout") + wrappedErr := stoneerror.Wrap(dbErr, 5001, "Database experiment failed"). + WithMetadata("query", "SELECT * FROM chemical_elements") +} +``` + +## ๐Ÿ”ฌ Error Output Format + +```text +StoneError [4001] +โ”œโ”€ Message: Invalid chemical formula +โ”œโ”€ Time: 2023-07-15T14:30:45Z +โ”œโ”€ Metadata: +โ”‚ โ”œโ”€ element: H2O2 +โ”‚ โ”œโ”€ expected: H2O +โ””โ”€ Caused by: + StoneError [5001] + โ”œโ”€ Message: Database experiment failed + โ”œโ”€ Time: 2023-07-15T14:30:45Z + โ”œโ”€ Metadata: + โ”‚ โ”œโ”€ query: SELECT * FROM chemical_elements + โ””โ”€ Caused by: + connection timeout +``` + +## ๐Ÿงช Core Features + +### Scientific Error Codes + +```go +const ( + LAB_FAILURE = 5000 // Critical experiment failure + BAD_CHEMISTRY = 4001 // Invalid formula or mixture + EQUIPMENT_FAIL = 5002 // Tools malfunction +) +``` + +### Advanced Error Wrapping + +```go +// Wrap any error with scientific context +result := performExperiment() +if err != nil { + return stoneerror.Wrap(err, LAB_FAILURE, "Voltaic pile test failed") + .WithMetadata("voltage", "3.7V") + .WithMetadata("materials", []string{"zinc", "copper"}) +} +``` + +### JSON Output + +```go +jsonError := err.ToJSON() +// { +// "code": 4001, +// "message": "Invalid chemical formula", +// "time": "2023-07-15T14:30:45Z", +// "metadata": { +// "element": "H2O2", +// "expected": "H2O" +// } +// } +``` + +```go +stonelog.InitStoneLab(stonelog.STONE_DEBUG, true, false) // true = JSON, false = Keep suffixes +// Output: {"time":"2023-07-15T12:00:00Z","level":"OBSERVATION","message":"โœ… Experiment successful: System ready","caller":"main.go:15"} +``` + +## โš—๏ธ Scientific Best Practices + +1. Always use specific error codes - Each experiment needs proper labeling! +2. Attach relevant metadata - A good scientist documents everything +3. Wrap underlying errors - Trace the full chain of causality +4. Combine related failures - Multiple data points lead to better conclusions + +**Join the Scientific Revolution!** + +> "With StoneError, we're 10 billion percent prepared for any failure!" - Senku Ishigami + +Kingdom of Science Approved + +(Now with 100% more Chrome screaming "SO BADASS!") \ No newline at end of file diff --git a/error.go b/error.go index 208d409..b465199 100644 --- a/error.go +++ b/error.go @@ -1,9 +1,19 @@ 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"` @@ -11,3 +21,83 @@ type StoneError struct { 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) +} diff --git a/go.mod b/go.mod index 5dfdcc5..60508e8 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module stoneerror +module gitstormr.dev/stone-utils/stoneerror go 1.24