Categorygithub.com/avila-r/failure
repositorypackage
1.0.0
Repository: https://github.com/avila-r/failure.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

failure.Error

*failure.Error implements the error interface and can be handled like a native error in your code.

func (e *Error) Error() string {
	return e.Message
}

If you want to create native errors, use the failure.Err() function. You can create both static and dynamic errors as follows:

// Static message, allowing error matching
err := failure.Err("error message!")
if err != nil {
	failure.Is(err, failure.Err("error message!")) // true
}

// Dynamic message, making error matching impossible
err := failure.Err("error message with arg %v!", 1)
if err != nil {
	failure.Is(err, failure.Err("error message with arg %v!", 2)) // false
}

Otherwise, failure.Error is a more powerful and versatile error type:

err := failure.Of("message, arg %v", 1)
other := failure.Of("message, arg %v", 2)

if failure.As(err, other) { // True
	// ...
}

You can enhance your errors by decorating them:

var (
	ErrNotFound = failure.New("user wasn't found")
)

func Find(id int, out *User) error {
	return ErrNotFound
}

func Delete(id int) error {
	user := User{}
	if err := Find(id, &user); err != nil {
		return failure.Decorate(err, "unable to delete user")
	}
}

//

if err := users.Delete(1); err != nil {
	log.Fatalf("Failed: %+v", err) // Includes stacktrace
}

You can organize your errors into namespaces, classes, and traits:

var (
	UserErrors = failure.Class("user")
	
	ErrNotFound = UserErrors.New("user wasn't found")
)

if err := Find(); failure.Is(err, ErrNotFound) /* or ErrNotFound.Is(err) */ {
	// ...
}

// or

if err := Find(); failure.Extends(err, UserErrors) {
	// ...
}
var (
	UserErrors = failure.Class("user")
	
	NotFound = UserErrors.Class("not_found", trait.NotFound)
)

Find := func() error {
	return NotFound.New("unable to find user with the provided email")
}

if err := Find(); failure.Extends(err, NotFound) {
	// ...
}

// or

if err := Find(); err != nil {
	return failure.Decorate(err, "failed to delete user")
}

// or

if err := Find(); failure.Has(err, trait.NotFound) {
	// ...
}

To enrich errors with additional metadata, use the ErrorChain methods:

tags := failure.Tags{
	"critical": "true",
}

err := failure.Of("operation failed").
	Chain()
	Owner("admin").
	Public("user-facing message").
	Hint("check network connection").
	Trace("trace-id-123").
	Tags(tags).
	Done()

fmt.Printf("%+v\n", err)

Since failure.Error implements slog.LogValuer, it can be logged directly:

import (
    "log/slog"
)

logger := slog.Default()

err := failure.Of("database connection failed").
	Chain()
	In("db").
	Owner("backend-team").
	Trace("trace-xyz").
	Done()

logger.Error("An error occurred", "error", err)

Properties:

Properties can be used to encapsulate additional payload/context or details for your errors. For example:

import (
	"github.com/avila-r/failure"
	"github.com/avila-r/failure/property"
)

var (
	ErrNotFound = failure.
		New("user not found").
		With(property.StatusCode, http.StatusNotFound) // Additional payload
)

func FindByID(id int) (*User, error) {
	return nil, ErrNotFound
}

if _, err := FindByID(id); err != nil {
	code := failure.Extract[int](err, property.StatusCode)
	// ...
}

failure.Property(err error, key string) returns a property.Result:

type Result struct {
	Value any
	Ok    bool
}

func (r Result) Get() (value any, ok bool)

func (r Result) Bind(out any) bool

This allows for better result handling:

result := failure.Property(err, "key")

value := 0
if ok := result.Bind(&value); !ok {
	// Handle
}

The function failure.Extract[T]() is also available to unwrap errors' properties:

ErrNotFound := failure.
	Of("user not found").
	With(property.StatusCode, http.StatusNotFound)

code := failure.Extract[int](ErrNotFound, "code")

// ...