Release v1.0.0 (#2)
Reviewed-on: #2 Reviewed-by: Cloud Administrator <cloud-admin@noreply.gitstormr.dev> Co-authored-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev> Co-committed-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev>
This commit is contained in:
parent
e65fc49b8e
commit
df0f48d7cb
123
README.md
123
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! 🚀
|
The most scientifically precise error library from the Kingdom of Science!
|
||||||
|
|
||||||
|
[]()
|
||||||
|
[]()
|
||||||
|
[]()
|
||||||
|
|
||||||
|
## 🚀 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!")
|
90
error.go
90
error.go
@ -1,9 +1,19 @@
|
|||||||
package stoneerror
|
package stoneerror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"time"
|
"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 {
|
type StoneError struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
@ -11,3 +21,83 @@ type StoneError struct {
|
|||||||
Metadata map[string]interface{} `json:"metadata"`
|
Metadata map[string]interface{} `json:"metadata"`
|
||||||
Err error `json:"-"`
|
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)
|
||||||
|
}
|
||||||
|
79
error_test.go
Normal file
79
error_test.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
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_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").
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user