err

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2021 License: MIT Imports: 2 Imported by: 12

README

go-error

err is a package with utility types to handle errors as constants and collections.

The idea is to define errors as string constants, using simple comparisons to check the kind of error raised and having an error aggregator when an operation may return more than one error (for example in validation functions) but still allowing individual errors that may have occurred.

Error

Defining simple static errors is as simple as defining a string constant

const errIncorrectTime = err.Error("incorrect time of day")

As an error it can be simply returned.

ErrorF

ErrorF introduces the concept of "Kind of Error". This basically serves to define a base type of error with custom messages related to the context where the error was raised, while still being able to identify that an error of a certain kind has been returned. The use is mainly for logging errors or using the error message for some operation, while still being able to handle the error at a generic level.

ErrorF itself is only meant to be used to generate error instances with custom messages (See Caveats).

const errUnknownPerson = err.ErrorF("Unknown Person (%s)")

func doSomething() error {
	return errUnknownPerson.WithValues("John Doe")
}

func main() {
	if e := doSomething; errUnknownPerson.IsKindOf(e) {
	    fmt.Println(e.Error()) // will print Unknown Person (John Dow)
    }
}

Errors

Errors is a collection of errors. It is meant to aggregate more than one error that a function may return when each one is not meant to be critical but the total amount of errors might. This would be the example for a validation function where it is desired to test all the validation failures instead of returning error on the first error occurrence.

Checking for error containment works both for Error and ErrorF kind of errors.

const (
    errIncorrectTime = err.Error("incorrect time of day")
    errUnknownPerson = err.ErrorF("Unknown Person (%s)")
)

func doSomething() error {
	e := err.Errors()
	e.AddError(errIncorrectTime)
	e.AddError(errUnknownPerson.WithValues("Someone"))
	e.Add("some error")
	return e
}

func main() {
	if e := doSomething; e != nil {
		errors := e.(err.IErrors)
		fmt.Println(errors.Contains(errIncorrectTime)) // prints true 
		fmt.Println(errors.Contains(errUnknownPerson)) // prints true 
		fmt.Println(errors.Contains(err.Error("some error"))) // prints true 
		fmt.Println(errors.Contains(err.Error("some other error"))) // prints false
	}
}

When checking for the presence of an error in a collection it is also possible to use the utility function IsContainedIn, where the main function from the previous example would be. The utility function Count(e error) int can also be used to count errors of a potential Errors object without the need to assert its type first.

func main() {
	if e := doSomething; e != nil {
		errors := e.(err.IErrors)
		fmt.Println(IsContainedIn(errIncorrectTime,errors)) // prints true 
		fmt.Println(IsContainedIn(errUnknownPerson,errors)) // prints true 
		fmt.Println(IsContainedIn(err.Error("some error"),errors)) // prints true 
		fmt.Println(IsContainedIn(err.Error("some other error"),errors)) // prints false
	}
}

Example

A simplistic usage is shown in the following example

package main

import (
	"fmt"
	"os"
	"regexp"

	"github.com/gomatbase/go-error"
)

var (
	greetings = make(map[string]string)
	people    = make(map[string]bool)
)

const (
	errIncorrectTime = err.Error("incorrect time of day")
	errInvalidName   = err.Error("invalid name format")
	errUnknownPerson = err.ErrorF("Unknown Person (%s)")
)

func init() {
	greetings["morning"] = "Good Morning"
	greetings["afternoon"] = "Good Afternoon"
	greetings["evening"] = "Good Evening"
	people["Jack"] = true
	people["Mary"] = true
}

func getGreeting(time string) (string, error) {
	if greeting, found := greetings[time]; found {
		return greeting, nil
	}
	return "", errIncorrectTime
}

func validateName(name string) error {
	e := err.Errors()
	r, _ := regexp.Compile("[A-Z][a-z]*")
	if !r.MatchString(name) {
		e.AddError(errInvalidName)
	}
	if _, found := people[name]; !found {
		e.AddError(errUnknownPerson.WithValues(name))
	}
	if e.Count() > 0 {
		return e
	}
	return nil
}

func main() {
	if len(os.Args) != 3 {
		fmt.Println("Invalid Number of Parameters")
		os.Exit(1)
	}
	greeting, e := getGreeting(os.Args[1])
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}

	name := os.Args[2]
	if e := validateName(name); e != nil {
		if err.IsContainedIn(errUnknownPerson,e) && err.Count(e) == 1 {
			name = e.Error()
		} else {
			fmt.Print(e)
			os.Exit(1)
		}
	}

	fmt.Println(greeting, name)
}

Caveats

Some details to keep in mind

ErrorF as an error

ErrorF implements the error interface. This means it can be used as a standard error which will result in having an error message that will be the string format pattern. This also means that there is nothing preventing ErrorF to be used as a standard error constant, except that trying to find if that specific error is contained in an Errors collection will always fail as ErrorF will always try to compare to errors of which it is a kind of (will only match errorFInstances).

ErrorF (Non-)Uniqueness

ErrorF is in fact a string that represents a format pattern. This means that the rules for string comparison apply to it. This also means that any identical ErrorFs, even though they might be defined as two distinct constants (or variables) they are considered to be the same, and when checking for errors of the same kind, any errorF instance having the same ErrorF base format pattern will be considered to be of the same kind (check err_test for an example where that property is tested).

ErrorF instances.

ErrorF instances are objects referencing the originating string format pattern, and the actual values that should be applied to the pattern. The number of values in the pattern are of the responsibility of the developer. Instantiating a new ErrorF with not enough or too many values will not raise any error but will result into an error with the corresponding "(%!s(MISSING))" tokens for not enough values and "%!(EXTRA string=)" tokens for too many.

ErrorF containment checks for Errors

When checking the existence of an error within an Errors collection, if using an actual ErrorFInstance to check, it will always fail unless it's an identical errorFInstance (either itself or an errorFInstance with the same string format pattern and the same actual error message). This works as expected and errorFInstances are usually meant to be tested for their kindness.

Documentation

Overview

Package err provides simple structures to handle error constants and error aggregations

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Count added in v1.1.0

func Count(e error) int

func IsContainedIn added in v1.1.0

func IsContainedIn(e error, es error) bool

IsContainedIn Utility method to check if an error is contained in an Errors collection without having to assert the error type it returns true if errors is an instance of Errors and contains e, or if e is of type ErrorF returns true if errors is a kind of e, or returns e == errors. Meant to work for functions expecting to return go-error errors.

Types

type Error

type Error string

Error Simple error type for log errors

func (Error) Error

func (e Error) Error() string

Error returns the error message

type ErrorF added in v1.1.0

type ErrorF string

ErrorF an error Kind

func (ErrorF) Error added in v1.1.0

func (ef ErrorF) Error() string

Error returns the error message

func (ErrorF) IsKindOf added in v1.1.0

func (ef ErrorF) IsKindOf(e error) bool

IsKindOf checks if the err is a kind of then ErrorF

func (ErrorF) WithValues added in v1.1.0

func (ef ErrorF) WithValues(values ...interface{}) *errorFInstance

type IErrors added in v1.1.0

type IErrors interface {
	error
	Add(message string)
	AddError(e error)
	Contains(e error) bool
	Count() int
}

IErrors type of error containing multiple entries for batch processing and collection of full set of errors (instead of failing on the first error)

func Errors

func Errors() IErrors

Errors creates and returns a new IErrors object

Example
es := Errors()
es.AddError(sampleError1)
es.AddError(sampleError2)
es.AddError(sampleError3)
es.AddError(sampleErrorF.WithValues("something"))

fmt.Print(es.Error())
Output:


sample error 1
sample error 2
sample error 3
test something

Jump to

Keyboard shortcuts

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