From b42137701b608a7b99fee566b573a0dc7246a938 Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Date: Sat, 12 Apr 2025 17:24:44 -0600 Subject: [PATCH 1/3] 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 -- 2.39.5 From 24ea252c96d28b5d43fbb34adde6c113896b167b Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Date: Sat, 12 Apr 2025 17:42:47 -0600 Subject: [PATCH 2/3] Add unit tests for stoneerror package Introduce comprehensive tests for core functionalities including error creation, metadata handling, wrapping, JSON conversion, and type validation. These tests ensure robustness and reliability of the stoneerror package. --- error_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 error_test.go diff --git a/error_test.go b/error_test.go new file mode 100644 index 0000000..755e386 --- /dev/null +++ b/error_test.go @@ -0,0 +1,53 @@ +package stoneerror + +import ( + "errors" + "testing" +) + +func Test_StoneErrorWithoutMetadata(t *testing.T) { + stoneError := New(1, "test") + + t.Log(stoneError) +} + +func Test_StoneErrorWithMetadata(t *testing.T) { + stoneError := New(1, "test"). + WithMetadata("key", "value"). + WithMetadata("key2", "value2") + + t.Log(stoneError) +} + +func Test_NormalWrap(t *testing.T) { + normalError := errors.New("test") + stoneError := Wrap(normalError, 1, "test") + t.Log(stoneError) +} + +func Test_StoneWrap(t *testing.T) { + originalError := New(1, "test"). + WithMetadata("key", "value"). + WithMetadata("key2", "value2") + stoneError := Wrap(originalError, 1, "test") + t.Log(stoneError) +} + +func Test_ToJSON(t *testing.T) { + stoneError := New(1, "test"). + WithMetadata("key", "value"). + WithMetadata("key2", "value2") + jsonData := stoneError.ToJSON() + t.Log(string(jsonData)) +} + +func Test_IsStoneError(t *testing.T) { + stoneError := New(1, "test"). + WithMetadata("key", "value") + + if IsStoneError(stoneError) { + t.Log("Is stone error") + } else { + t.Fail() + } +} -- 2.39.5 From bcb4a3008d470599f25b578148d381015826d558 Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Date: Sat, 12 Apr 2025 17:53:48 -0600 Subject: [PATCH 3/3] Add unwrap tests for normal and stone errors Introduce tests to validate the behavior of the Unwrap function for both standard errors and StoneError instances. Ensure correct handling of wrapped errors and verify type identification with IsStoneError checks. --- error_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/error_test.go b/error_test.go index 755e386..65f9d7f 100644 --- a/error_test.go +++ b/error_test.go @@ -33,6 +33,32 @@ func Test_StoneWrap(t *testing.T) { t.Log(stoneError) } +func Test_NormalUnwrap(t *testing.T) { + normalError := errors.New("test") + stoneError := Wrap(normalError, 1, "test") + + unwrap := stoneError.Unwrap() + + if IsStoneError(unwrap) { + t.Fail() + } else { + t.Log(stoneError) + } +} + +func Test_StoneUnwrap(t *testing.T) { + normalError := New(1, "test") + stoneError := Wrap(normalError, 1, "test") + + unwrap := stoneError.Unwrap() + + if !IsStoneError(unwrap) { + t.Fail() + } else { + t.Log(stoneError) + } +} + func Test_ToJSON(t *testing.T) { stoneError := New(1, "test"). WithMetadata("key", "value"). -- 2.39.5