compat

package
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2026 License: MIT Imports: 1 Imported by: 0

README

compat - Standard Error Interface Compatibility

The compat package provides compatibility functions that accept standard Go error interface instead of requiring errx.Classified types. This package is designed for users who prefer working with the standard error interface while still benefiting from errx's classification and wrapping capabilities.

Why This Package Exists

The parent errx package uses the Classified interface for several important reasons:

  1. Type Safety: Ensures only valid classification types can be attached to errors
  2. Sealed Interface Pattern: Maintains controlled extensibility through the IsClassified() marker method
  3. API Stability: Allows internal evolution without breaking existing code
  4. Clear Intent: Makes it explicit that you're attaching metadata rather than wrapping arbitrary errors

However, some users prefer the flexibility of working with standard Go error interface. The compat package bridges this gap.

Quick Start

import (
    "errors"
    "github.com/go-extras/errx/compat"
)

// Define classification errors (can be any error type)
var (
    ErrNotFound   = errors.New("not found")
    ErrDatabase   = errors.New("database error")
    ErrValidation = errors.New("validation error")
)

// Use compat functions with standard errors
func fetchUser(id string) error {
    err := db.Query(id)
    if err != nil {
        return compat.Wrap("failed to fetch user", err, ErrNotFound, ErrDatabase)
    }
    return nil
}

// Check classifications using standard errors.Is
if errors.Is(err, ErrNotFound) {
    // Handle not found case
}

API

compat.Wrap(text string, cause error, classifications ...error) error

Wraps an error with additional context text and optional classifications. Accepts standard Go error interface for classifications.

err := db.Query(id)
return compat.Wrap("failed to fetch user", err, ErrNotFound, ErrDatabase)
compat.Classify(cause error, classifications ...error) error

Attaches classifications to an error without adding context text. Preserves the original error message.

err := validateEmail(email)
return compat.Classify(err, ErrValidation)

Mixing with errx Types

You can freely mix standard errors with errx.Classified types:

displayable := errx.NewDisplayable("User not found")
attrErr := errx.Attrs("user_id", 123)

err := compat.Wrap("lookup failed", baseErr, ErrNotFound, displayable, attrErr)

Stacktrace Integration

Since stacktrace functionality requires errx.Classified types, this package does NOT provide mirror functions for the stacktrace package. This is an intentional design decision.

If you need stack traces, you have two options:

  1. Use stacktrace.Here() explicitly:

    import "github.com/go-extras/errx/stacktrace"
    
    err := compat.Wrap("failed", cause, stacktrace.Here(), ErrDatabase)
    
  2. Use stacktrace package functions directly:

    err := stacktrace.Wrap("failed", cause, classification)
    

Tradeoffs

Advantages
  • Works with any error type, including third-party errors
  • More flexible for codebases that heavily use standard error interface
  • Easier migration path from existing error handling code
Disadvantages
  • Less type safety - you can accidentally pass non-classification errors
  • Slightly more overhead due to additional wrapping layer
  • Less clear intent - harder to distinguish classification metadata from regular errors

Examples

Basic Classification
var ErrNotFound = errors.New("not found")

err := db.Get(id)
if err != nil {
    return compat.Classify(err, ErrNotFound)
}
Multiple Classifications
var (
    ErrDatabase  = errors.New("database error")
    ErrRetryable = errors.New("retryable error")
)

err := db.Transaction()
return compat.Wrap("transaction failed", err, ErrDatabase, ErrRetryable)
With Attributes
attrErr := errx.Attrs("user_id", 123, "action", "delete")
err := compat.Wrap("operation failed", baseErr, ErrDatabase, attrErr)

// Later, extract attributes for logging
if errx.HasAttrs(err) {
    attrs := errx.ExtractAttrs(err)
    logger.Error("error occurred", "attrs", attrs)
}
Chaining Calls
err1 := compat.Classify(baseErr, ErrDatabase)
err2 := compat.Wrap("layer 2", err1, ErrRetryable)
err3 := compat.Wrap("layer 3", err2)

// All classifications are preserved
errors.Is(err3, ErrDatabase)  // true
errors.Is(err3, ErrRetryable) // true

When to Use

Use the compat package when:

  • You're migrating existing code that uses standard errors
  • You prefer the flexibility of standard error interface
  • You're integrating with third-party libraries that use standard errors
  • You want to avoid the ceremony of creating errx.Classified types

Use the parent errx package when:

  • You want maximum type safety
  • You're building a new codebase from scratch
  • You want clear distinction between classifications and regular errors
  • You need the full power of the sealed interface pattern

See Also

Documentation

Overview

Package compat provides compatibility functions that accept standard Go error interface instead of requiring errx.Classified types. This package is designed for users who prefer working with the standard error interface while still benefiting from errx's classification and wrapping capabilities.

Why the Parent Package Uses errx.Classified

The parent errx package uses the Classified interface for several important reasons:

  1. Type Safety: The Classified interface ensures that only valid classification types (sentinels, displayable errors, attributed errors) can be attached to errors. This prevents accidental misuse and provides compile-time guarantees.

  2. Sealed Interface Pattern: The Classified interface uses a marker method (IsClassified) that allows the library to maintain controlled extensibility. External packages can implement Classified, but the library can identify and validate these implementations.

  3. API Stability: By requiring Classified types, the library can evolve its internal implementation without breaking existing code that depends on the classification behavior.

  4. Clear Intent: Using Classified makes it explicit that you're attaching metadata (classifications, displayable messages, attributes) rather than wrapping arbitrary errors in the classification chain.

How This Package Provides Flexibility

This compat package provides mirror functions that accept standard Go error interface:

  • compat.Wrap(text, cause, classifications...) accepts error classifications
  • compat.Classify(cause, classifications...) accepts error classifications

These functions internally convert the provided error values to errx.Classified types before calling the parent package functions. This conversion is done by wrapping each error in an errx.Classified wrapper that preserves the error's identity for errors.Is and errors.As checks.

Tradeoffs

Using this package involves some tradeoffs:

**Advantages:**

  • Works with any error type, including third-party errors
  • More flexible for codebases that heavily use standard error interface
  • Easier migration path from existing error handling code

**Disadvantages:**

  • Less type safety - you can accidentally pass non-classification errors
  • Slightly more overhead due to additional wrapping layer
  • Less clear intent - harder to distinguish classification metadata from regular errors

Stacktrace Integration

Since stacktrace functionality requires errx.Classified types, this package does NOT provide mirror functions for the stacktrace package. This is an intentional design decision. If you need stack traces, you have two options:

  1. Use stacktrace.Here() explicitly in your compat calls: err := compat.Wrap("failed", cause, stacktrace.Here())

  2. Use the stacktrace package functions directly: err := stacktrace.Wrap("failed", cause, classification)

Example Usage

// Define classification errors (can be any error type)
var ErrNotFound = errors.New("not found")
var ErrInvalid = errors.New("invalid input")

// Use compat functions with standard errors
func fetchUser(id string) error {
    err := db.Query(id)
    if err != nil {
        return compat.Wrap("failed to fetch user", err, ErrNotFound)
    }
    return nil
}

// Check classifications using standard errors.Is
if errors.Is(err, ErrNotFound) {
    // Handle not found case
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Classify

func Classify(cause error, classifications ...error) error

Classify attaches one or more classifications to an existing error without adding context text. This is a compatibility function that accepts standard Go error interface for classifications instead of requiring errx.Classified types.

The function internally converts the provided error classifications to errx.Classified types before calling errx.Classify. This allows you to use any error type as a classification, including third-party errors and standard library errors.

If cause is nil, Classify returns nil.

Example:

var ErrValidation = errors.New("validation error")

err := validateInput(data)
return compat.Classify(err, ErrValidation)

// Later, check with errors.Is
if errors.Is(err, ErrValidation) {
    // Handle validation error
}
Example

ExampleClassify demonstrates using compat.Classify with standard errors

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification error
	var ErrValidation = errors.New("validation error")

	// Error with a clear message that doesn't need additional context
	err := errors.New("email format is invalid")

	// Classify without changing the message
	classified := compat.Classify(err, ErrValidation)

	fmt.Println(classified.Error())
	fmt.Println("Is validation error:", errors.Is(classified, ErrValidation))

}
Output:
email format is invalid
Is validation error: true
Example (MultipleClassifications)

ExampleClassify_multipleClassifications demonstrates multiple classifications

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification errors
	var (
		ErrDatabase  = errors.New("database error")
		ErrRetryable = errors.New("retryable error")
	)

	dbErr := errors.New("deadlock detected")

	// Attach multiple classifications
	err := compat.Classify(dbErr, ErrDatabase, ErrRetryable)

	fmt.Println(err.Error())
	fmt.Println("Is database error:", errors.Is(err, ErrDatabase))
	fmt.Println("Is retryable:", errors.Is(err, ErrRetryable))

}
Output:
deadlock detected
Is database error: true
Is retryable: true
Example (NilCause)

ExampleClassify_nilCause demonstrates nil handling for Classify

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification error
	var ErrValidation = errors.New("validation error")

	// Classify returns nil when cause is nil
	err := compat.Classify(nil, ErrValidation)

	fmt.Println("Error is nil:", err == nil)

}
Output:
Error is nil: true

func ClassifyNew added in v1.2.0

func ClassifyNew(text string, classifications ...error) error

ClassifyNew creates a new error with the given text and immediately classifies it with one or more classifications. This is a convenience function equivalent to calling compat.Classify(errors.New(text), classifications...).

This function accepts standard Go error interface for classifications instead of requiring errx.Classified types, making it compatible with any error type including third-party errors and standard library errors.

This function is useful when you want to create a new error and classify it in a single step, reducing verbosity compared to the two-step approach.

Example:

var ErrNotFound = errors.New("not found")
var ErrDatabase = errors.New("database error")

// Instead of:
// err := compat.Classify(errors.New("user record missing"), ErrNotFound, ErrDatabase)

// You can write:
err := compat.ClassifyNew("user record missing", ErrNotFound, ErrDatabase)

fmt.Println(err.Error())                        // Output: user record missing
fmt.Println(errors.Is(err, ErrNotFound))        // Output: true
fmt.Println(errors.Is(err, ErrDatabase))        // Output: true
Example

ExampleClassifyNew demonstrates creating and classifying an error in one step

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification errors
	var (
		ErrDatabase  = errors.New("database error")
		ErrRetryable = errors.New("retryable error")
	)

	// Create a new error and classify it in one step
	err := compat.ClassifyNew("connection timeout", ErrDatabase, ErrRetryable)

	fmt.Println(err.Error())
	fmt.Println("Is database error:", errors.Is(err, ErrDatabase))
	fmt.Println("Is retryable:", errors.Is(err, ErrRetryable))

}
Output:
connection timeout
Is database error: true
Is retryable: true
Example (WithErrxTypes)

ExampleClassifyNew_withErrxTypes demonstrates ClassifyNew with errx types

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/compat"
)

func main() {
	// Mix standard errors with errx types
	var ErrNotFound = errors.New("not found")

	displayable := errx.NewDisplayable("The requested user does not exist")
	attrErr := errx.Attrs("user_id", 12345, "table", "users")

	err := compat.ClassifyNew("user record missing from database", ErrNotFound, displayable, attrErr)

	fmt.Println("Error:", err.Error())
	fmt.Println("Display text:", errx.DisplayText(err))
	fmt.Println("Is not found:", errors.Is(err, ErrNotFound))
	fmt.Println("Has attributes:", errx.HasAttrs(err))

}
Output:
Error: user record missing from database
Display text: The requested user does not exist
Is not found: true
Has attributes: true

func Wrap

func Wrap(text string, cause error, classifications ...error) error

Wrap wraps an error with additional context text and optional classifications. This is a compatibility function that accepts standard Go error interface for classifications instead of requiring errx.Classified types.

The function internally converts the provided error classifications to errx.Classified types before calling errx.Wrap. This allows you to use any error type as a classification, including third-party errors and standard library errors.

If cause is nil, Wrap returns nil.

Example:

var ErrNotFound = errors.New("not found")
var ErrDatabase = errors.New("database error")

err := db.Query(id)
return compat.Wrap("failed to fetch user", err, ErrNotFound, ErrDatabase)

// Later, check with errors.Is
if errors.Is(err, ErrNotFound) {
    // Handle not found case
}
Example

ExampleWrap demonstrates using compat.Wrap with standard errors

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification errors
	var (
		ErrDatabase  = errors.New("database error")
		ErrRetryable = errors.New("retryable error")
	)

	// Simulate a database error
	dbErr := errors.New("connection timeout")

	// Wrap with context and classifications using standard errors
	err := compat.Wrap("failed to fetch user", dbErr, ErrDatabase, ErrRetryable)

	fmt.Println(err.Error())
	fmt.Println("Is database error:", errors.Is(err, ErrDatabase))
	fmt.Println("Is retryable:", errors.Is(err, ErrRetryable))

}
Output:
failed to fetch user: connection timeout
Is database error: true
Is retryable: true
Example (Chaining)

ExampleWrap_chaining demonstrates chaining compat calls

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification errors
	var (
		ErrDatabase  = errors.New("database error")
		ErrRetryable = errors.New("retryable error")
	)

	// Start with a base error
	baseErr := errors.New("disk full")

	// Layer 1: Classify as database error
	err1 := compat.Classify(baseErr, ErrDatabase)

	// Layer 2: Add context and mark as retryable
	err2 := compat.Wrap("failed to write transaction log", err1, ErrRetryable)

	// Layer 3: Add more context
	err3 := compat.Wrap("transaction commit failed", err2)

	fmt.Println(err3.Error())
	fmt.Println("Is database error:", errors.Is(err3, ErrDatabase))
	fmt.Println("Is retryable:", errors.Is(err3, ErrRetryable))

}
Output:
transaction commit failed: failed to write transaction log: disk full
Is database error: true
Is retryable: true
Example (NilCause)

ExampleWrap_nilCause demonstrates nil handling

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification error
	var ErrNotFound = errors.New("not found")

	// Wrap returns nil when cause is nil
	err := compat.Wrap("context", nil, ErrNotFound)

	fmt.Println("Error is nil:", err == nil)

}
Output:
Error is nil: true
Example (WithAttributes)

ExampleWrap_withAttributes demonstrates using attributes with compat

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification error
	var ErrDatabase = errors.New("database error")

	baseErr := errors.New("query timeout")

	// Create attributed error for structured logging
	attrErr := errx.Attrs(
		"query", "SELECT * FROM users WHERE id = ?",
		"timeout_ms", 5000,
		"retry_count", 3,
	)

	err := compat.Wrap("database query failed", baseErr, ErrDatabase, attrErr)

	// Extract attributes for logging
	if errx.HasAttrs(err) {
		attrs := errx.ExtractAttrs(err)
		fmt.Println("Error:", err.Error())
		fmt.Println("Attributes:", len(attrs))
		for _, attr := range attrs {
			fmt.Printf("  %s: %v\n", attr.Key, attr.Value)
		}
	}

}
Output:
Error: database query failed: query timeout
Attributes: 3
  query: SELECT * FROM users WHERE id = ?
  timeout_ms: 5000
  retry_count: 3
Example (WithErrxTypes)

ExampleWrap_withErrxTypes demonstrates mixing standard errors with errx types

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/compat"
)

func main() {
	// Define classification error
	var ErrNotFound = errors.New("not found")

	baseErr := errors.New("user not found in database")

	// Mix standard errors with errx types
	displayable := errx.NewDisplayable("The requested user does not exist")
	attrErr := errx.Attrs("user_id", 12345, "table", "users")

	err := compat.Wrap("lookup failed", baseErr, ErrNotFound, displayable, attrErr)

	fmt.Println(err.Error())
	fmt.Println("Is not found:", errors.Is(err, ErrNotFound))
	fmt.Println("Has attributes:", errx.HasAttrs(err))

}
Output:
lookup failed: user not found in database
Is not found: true
Has attributes: true

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL