Skip to content
failure is a utility package for handling application errors.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci
.github
.codecov.yml
.gitignore
LICENSE
Makefile
README.md
callstack.go
callstack_test.go
code.go
code_test.go
failure.go
failure_test.go
go.mod
go.sum
iterator.go
iterator_test.go
wrapper.go

README.md

failure

CircleCI GoDoc Go Report Card codecov

Package failure provides an error represented as error code and extensible error interface with wrappers.

Use error code instead of error type.

var NotFound failure.StringCode = "NotFound"

err := failure.New(NotFound)

if failure.Is(err, NotFound) { // true
	r.WriteHeader(http.StatusNotFound)
}

Wrap errors.

type Wrapper interface {
	WrapError(err error) error
}

err := failure.Wrap(err, MarkTemporary())

Unwrap errors with Iterator.

type Unwrapper interface {
	UnwrapError() error
}

i := failure.NewIterator(err)
for i.Next() { // unwrap error
	err := i.Error()
	if e, ok := err.(Temporary); ok {
		return e.IsTemporary()
	}
}

Example

package main

import (
	"errors"
	"fmt"
	"io"
	"net/http"

	"github.com/morikuni/failure"
)

// error codes for your application.
const (
	NotFound  failure.StringCode = "NotFound"
	Forbidden failure.StringCode = "Forbidden"
)

func GetACL(projectID, userID string) (acl interface{}, e error) {
	notFound := true
	if notFound {
		return nil, failure.New(NotFound,
			failure.MessageKV{"project_id": projectID, "user_id": userID},
		)
	}
	err := failure.Unexpected("unexpected error")
	if err != nil {
		return nil, failure.Wrap(err)
	}
	return nil, nil
}

func GetProject(projectID, userID string) (project interface{}, e error) {
	_, err := GetACL(projectID, userID)
	if err != nil {
		if failure.Is(err, NotFound) {
			return nil, failure.Translate(err, Forbidden,
				failure.Message("no acl exists"),
				failure.MessageKV{"additional_info": "hello"},
			)
		}
		return nil, failure.Wrap(err)
	}
	return nil, nil
}

func Handler(w http.ResponseWriter, r *http.Request) {
	_, err := GetProject(r.FormValue("projectID"), r.FormValue("userID"))
	if err != nil {
		HandleError(w, err)
		return
	}
}

func getHTTPStatus(err error) int {
	c, ok := failure.CodeOf(err)
	if !ok {
		return http.StatusInternalServerError
	}
	switch c {
	case NotFound:
		return http.StatusNotFound
	case Forbidden:
		return http.StatusForbidden
	default:
		return http.StatusInternalServerError
	}
}

func getMessage(err error) string {
	msg, ok := failure.MessageOf(err)
	if ok {
		return msg
	}
	return "Error"
}

func HandleError(w http.ResponseWriter, err error) {
	w.WriteHeader(getHTTPStatus(err))
	io.WriteString(w, getMessage(err))

	fmt.Println(err)
	// main.GetProject: no acl exists: additional_info=hello: code(Forbidden): main.GetACL: project_id=123 user_id=456: code(NotFound)
	fmt.Printf("%+v\n", err)
	// [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:36
	//     message("no acl exists")
	//     additional_info = hello
	//     code(Forbidden)
	// [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
	//     project_id = 123
	//     user_id = 456
	//     code(NotFound)
	// [CallStack]
	//     [main.GetACL] /go/src/github.com/morikuni/failure/example/main.go:21
	//     [main.GetProject] /go/src/github.com/morikuni/failure/example/main.go:33
	//     [main.Handler] /go/src/github.com/morikuni/failure/example/main.go:47
	//     [http.HandlerFunc.ServeHTTP] /usr/local/go/src/net/http/server.go:1964
	//     [http.(*ServeMux).ServeHTTP] /usr/local/go/src/net/http/server.go:2361
	//     [http.serverHandler.ServeHTTP] /usr/local/go/src/net/http/server.go:2741
	//     [http.(*conn).serve] /usr/local/go/src/net/http/server.go:1847
	//     [runtime.goexit] /usr/local/go/src/runtime/asm_amd64.s:1333
	fmt.Println(failure.CodeOf(err))
	// Forbidden true
	fmt.Println(failure.MessageOf(err))
	// no acl exists true
	fmt.Println(failure.CallStackOf(err))
	// main.GetACL: main.GetProject: main.Handler: http.HandlerFunc.ServeHTTP: http.(*ServeMux).ServeHTTP: http.serverHandler.ServeHTTP: http.(*conn).serve: goexit true
	fmt.Println(failure.CauseOf(err))
	// code(NotFound)
}

func main() {
	http.HandleFunc("/", Handler)
	http.ListenAndServe(":8080", nil)
	// $ http "localhost:8080/?projectID=123&userID=456"
	//
	// HTTP/1.1 403 Forbidden
	// Content-Length: 13
	// Content-Type: text/plain; charset=utf-8
	// Date: Sat, 16 Feb 2019 14:02:30 GMT
	//
	// no acl exists
}
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.