package errors import ( "fmt" "runtime" "time" ) // ErrorCategory represents the category of an error type ErrorCategory string const ( CategoryNetwork ErrorCategory = "NETWORK" // RPC, DNS, connection issues CategoryParsing ErrorCategory = "PARSING" // ABI decoding, transaction parsing CategoryValidation ErrorCategory = "VALIDATION" // Input validation, data validation CategoryExecution ErrorCategory = "EXECUTION" // Transaction execution, contract calls CategoryConfiguration ErrorCategory = "CONFIGURATION" // Config loading, invalid settings CategoryDatabase ErrorCategory = "DATABASE" // Database operations CategorySecurity ErrorCategory = "SECURITY" // Security violations, unauthorized access CategoryMath ErrorCategory = "MATH" // Arithmetic errors, overflow/underflow CategoryInternal ErrorCategory = "INTERNAL" // Internal logic errors, unexpected state CategoryExternal ErrorCategory = "EXTERNAL" // External service failures CategoryUnknown ErrorCategory = "UNKNOWN" // Uncategorized errors ) // ErrorSeverity represents how critical an error is type ErrorSeverity string const ( SeverityDebug ErrorSeverity = "DEBUG" // Informational, not an actual error SeverityInfo ErrorSeverity = "INFO" // Notable but not problematic SeverityWarning ErrorSeverity = "WARNING" // Potential issue, should investigate SeverityError ErrorSeverity = "ERROR" // Actual error, functionality impacted SeverityCritical ErrorSeverity = "CRITICAL" // Critical error, immediate attention required SeverityFatal ErrorSeverity = "FATAL" // Fatal error, system cannot continue ) // StructuredError represents a comprehensive error with full context type StructuredError struct { // Core error information Message string // Human-readable error message Category ErrorCategory // Error category for classification Severity ErrorSeverity // How critical is this error // Origin tracking File string // Source file where error occurred Function string // Function where error occurred Line int // Line number where error occurred Package string // Go package where error occurred // Context Reason string // Why this error occurred (root cause) Action string // What the code was trying to do when it failed Impact string // Impact of this error on the system Suggestion string // Suggested fix or next steps Details map[string]interface{} // Additional structured context UnderlyingErr error // Original error if wrapping // Metadata Timestamp time.Time // When the error occurred ErrorID string // Unique identifier for this error instance } // Error implements the error interface func (e *StructuredError) Error() string { if e.UnderlyingErr != nil { return fmt.Sprintf("[%s/%s] %s: %v (in %s:%d)", e.Category, e.Severity, e.Message, e.UnderlyingErr, e.File, e.Line) } return fmt.Sprintf("[%s/%s] %s (in %s:%d)", e.Category, e.Severity, e.Message, e.File, e.Line) } // Unwrap returns the underlying error for error chain support func (e *StructuredError) Unwrap() error { return e.UnderlyingErr } // NewStructuredError creates a new structured error with automatic caller tracking func NewStructuredError(category ErrorCategory, severity ErrorSeverity, message string) *StructuredError { return newStructuredErrorWithDepth(category, severity, message, 2) } // newStructuredErrorWithDepth creates a structured error with custom stack depth func newStructuredErrorWithDepth(category ErrorCategory, severity ErrorSeverity, message string, depth int) *StructuredError { // Get caller information pc, file, line, ok := runtime.Caller(depth) if !ok { file = "unknown" line = 0 } funcName := "unknown" packageName := "unknown" if fn := runtime.FuncForPC(pc); fn != nil { funcName = fn.Name() // Extract package name from function name (format: "package/path.FunctionName") for i := len(funcName) - 1; i >= 0; i-- { if funcName[i] == '/' { packageName = funcName[:i] break } } } // Generate unique error ID errorID := fmt.Sprintf("ERR-%d-%s", time.Now().UnixNano(), category) return &StructuredError{ Message: message, Category: category, Severity: severity, File: file, Function: funcName, Line: line, Package: packageName, Details: make(map[string]interface{}), Timestamp: time.Now(), ErrorID: errorID, } } // WithReason adds the reason (root cause) for the error func (e *StructuredError) WithReason(reason string) *StructuredError { e.Reason = reason return e } // WithAction describes what the code was trying to do func (e *StructuredError) WithAction(action string) *StructuredError { e.Action = action return e } // WithImpact describes the impact of this error func (e *StructuredError) WithImpact(impact string) *StructuredError { e.Impact = impact return e } // WithSuggestion provides a suggestion for fixing or handling the error func (e *StructuredError) WithSuggestion(suggestion string) *StructuredError { e.Suggestion = suggestion return e } // WithDetail adds a key-value detail to the error context func (e *StructuredError) WithDetail(key string, value interface{}) *StructuredError { e.Details[key] = value return e } // WithDetails adds multiple details at once func (e *StructuredError) WithDetails(details map[string]interface{}) *StructuredError { for k, v := range details { e.Details[k] = v } return e } // Wrap wraps an underlying error with structured context func (e *StructuredError) Wrap(err error) *StructuredError { e.UnderlyingErr = err return e } // FormatForLogging returns a comprehensive string representation for logging func (e *StructuredError) FormatForLogging() string { result := fmt.Sprintf( "[%s] %s/%s: %s\n"+ " Origin: %s:%d (%s)\n"+ " ErrorID: %s\n"+ " Timestamp: %s\n", e.ErrorID, e.Category, e.Severity, e.Message, e.File, e.Line, e.Function, e.ErrorID, e.Timestamp.Format(time.RFC3339), ) if e.Reason != "" { result += fmt.Sprintf(" Reason: %s\n", e.Reason) } if e.Action != "" { result += fmt.Sprintf(" Action: %s\n", e.Action) } if e.Impact != "" { result += fmt.Sprintf(" Impact: %s\n", e.Impact) } if e.Suggestion != "" { result += fmt.Sprintf(" Suggestion: %s\n", e.Suggestion) } if len(e.Details) > 0 { result += " Details:\n" for k, v := range e.Details { result += fmt.Sprintf(" - %s: %v\n", k, v) } } if e.UnderlyingErr != nil { result += fmt.Sprintf(" Underlying: %v\n", e.UnderlyingErr) } return result } // FormatCompact returns a single-line representation func (e *StructuredError) FormatCompact() string { compact := fmt.Sprintf("[%s/%s] %s", e.Category, e.Severity, e.Message) if e.Reason != "" { compact += fmt.Sprintf(" | Reason: %s", e.Reason) } if e.Action != "" { compact += fmt.Sprintf(" | Action: %s", e.Action) } compact += fmt.Sprintf(" | Origin: %s:%d", e.File, e.Line) if e.UnderlyingErr != nil { compact += fmt.Sprintf(" | Underlying: %v", e.UnderlyingErr) } return compact } // Helper functions for common error patterns // NetworkError creates a network-related error func NetworkError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryNetwork, SeverityError, message, 2) } // ParsingError creates a parsing-related error func ParsingError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryParsing, SeverityError, message, 2) } // ValidationError creates a validation-related error func ValidationError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryValidation, SeverityWarning, message, 2) } // ExecutionError creates an execution-related error func ExecutionError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryExecution, SeverityCritical, message, 2) } // ConfigurationError creates a configuration-related error func ConfigurationError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryConfiguration, SeverityCritical, message, 2) } // InternalError creates an internal logic error func InternalError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryInternal, SeverityError, message, 2) } // MathError creates a mathematical error func MathError(message string) *StructuredError { return newStructuredErrorWithDepth(CategoryMath, SeverityError, message, 2) } // SecurityError creates a security-related error func SecurityError(message string) *StructuredError { return newStructuredErrorWithDepth(CategorySecurity, SeverityCritical, message, 2) }