customerror

package module
v1.2.9 Latest Latest
Warning

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

Go to latest
Published: Oct 28, 2024 License: MIT Imports: 10 Imported by: 55

README

customerror

customerror provides the base block to create custom errors. It also provides built-in custom errors covering some common cases. A Custom Error provides context - a Message to an optionally wrapped Err. Additionally a Code - for example "E1010", and StatusCode can be provided. Both static (pre-created), and dynamic (in-line) errors can be easily created. Code helps a company build a catalog of errors, which helps, and improves customer service.

Install

$ go get github.com/thalesfsp/customerror@vX.Y.Z

Usage

See example_test.go, and customerror_test.go file.

Documentation

Run $ make doc or check out online.

Development

Check out CONTRIBUTION.

Release
  1. Update CHANGELOG accordingly.
  2. Once changes from MR are merged.
  3. Tag and release.

Roadmap

Check out CHANGELOG.

Documentation

Overview

Package customerror provides the base block to create custom errors. It also provides built-in custom errors covering some common cases. A Custom Error provides context - a `Message` to an optionally wrapped `Err`. Additionally a `Code` - for example "E1010", and `StatusCode` can be provided. Both static (pre-created), and dynamic (in-line) errors can be easily created. `Code` helps a company build a catalog of errors, which helps, and improves customer service.

Examples:

See `example_test.go` or the Example section of the GoDoc documention.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrCatalogErrorNotFound is returned when a custom error isn't found in
	// the catalog.
	ErrCatalogErrorNotFound = NewNotFoundError("error", WithErrorCode("CE_ERR_CATALOG_ERR_NOT_FOUND"))

	// ErrCatalogInvalidName is returned when a catalog name is invalid.
	ErrCatalogInvalidName = NewInvalidError("name", WithErrorCode("CE_ERR_CATALOG_INVALID_NAME"))

	// ErrErrorCodeInvalidCode is returned when an error code is invalid.
	ErrErrorCodeInvalidCode = NewInvalidError("error code. It requires typeOf, and subject", WithErrorCode("CE_ERR_INVALID_ERROR_CODE"))

	// ErrorCodeRegex is a regular expression to validate error codes. It's
	// designed to match four distinct patterns:
	//
	// 1. An optional number and letter followed by an underscore, "ERR_", a
	// letter and a number, another underscore, another letter and a number, and
	// an optional underscore followed by a number and a letter:
	// Format: {optional_number_letter}_ERR_{number_letter}_{number_letter}_{optional_number_letter}
	// Example: 1A_ERR_A1_B2 or 1A_ERR_A1_B2_3C
	//
	// 2. "ERR_" followed by a letter and a number, another underscore, another
	// letter and a number, and an optional underscore followed by a number and
	// a letter:
	// Format: ERR_{number_letter}_{number_letter}_{optional_number_letter}
	// Example: ERR_A1_B2 or ERR_A1_B2_3C
	//
	// 3. "E" followed by 1 to 8 digits:
	// Format: E{1 to 8 digits}
	// Example: E12345678
	//
	// 4. At least one letter or number (any combination of uppercase and
	// lowercase letters and digits):
	// Format: {letters or digits, at least one character}
	// Example: AbCd123.
	ErrorCodeRegex = regexp.MustCompile(`^(\d?[A-Za-z]_)?ERR_[A-Za-z]\d_[A-Za-z]\d(_\d[A-Za-z])?$|^ERR_[A-Za-z]\d_[A-Za-z]\d(_\d[A-Za-z])?$|^E\d{1,8}$|[A-Za-z\d]+`)
)
View Source
var (
	// ErrInvalidLanguageCode is returned when a language code is invalid.
	ErrInvalidLanguageCode = NewInvalidError("it must be a string, two-letter lowercase ISO 639-1 code OR two-letter lowercase ISO 639-1 code followed by an optional hyphen AND a two-letter uppercase ISO 3166-1 alpha-2 country code", WithErrorCode("CE_ERR_INVALID_LANG_CODE"))

	// ErrInvalidLanguageErrorMessage is returned when an error message is invalid.
	ErrInvalidLanguageErrorMessage = NewInvalidError("it must be a string, at least 3 characters long", WithErrorCode("CE_ERR_INVALID_LANG_ERROR_MESSAGE"))

	// ErrInvalidLanguageMessageMap is returned when a LanguageMessageMap is
	// invalid.
	ErrInvalidLanguageMessageMap = NewInvalidError("it must be a non-nil map of language codes to error messages", WithErrorCode("CE_ERR_INVALID_LANGUAGE_MESSAGE_MAP"))

	// BuiltInLanguages is a list of built-in prefixes languages.
	BuiltInLanguages = []string{
		Chinese.String(),
		English.String(),
		French.String(),
		German.String(),
		Italian.String(),
		Portuguese.String(),
		Spanish.String(),
	}

	// LanguageRegex is a regular expression to validate language codes based on
	// ISO 639-1 and ISO 3166-1 alpha-2.
	LanguageRegex = regexp.MustCompile("^[a-z]{2}(-[A-Z]{2})?$|default")
)
View Source
var (

	// ErrTemplateNotFound is returned when a requested error template is not
	// found in the map.
	ErrTemplateNotFound = NewNotFoundError(fmt.Sprintf(
		"%s. %s. Built-in languages: %s.",
		"template",
		"Please set one using `SetErrorPrefixMap`",
		strings.Join(BuiltInLanguages, ", "),
	), WithErrorCode("CE_ERR_TEMPLATE_NOT_FOUND"))

	// ErrLanguageNotFound is returned when a requested language is not supported.
	ErrLanguageNotFound = NewNotFoundError("language. Please set one using `SetErrorPrefixMap`", WithErrorCode("CE_ERR_LANGUAGE_NOT_FOUND"))
)

Singleton variables and error instances.

Functions

func AddNewLanguage added in v1.2.6

func AddNewLanguage(
	language string,
	errorTypePrefixTemplateMap ErrorPrefixMap,
) error

AddNewLanguage allows setting a new language to the error handling system, seeting the built-in error templates for each error type.

func From added in v1.2.8

func From(err error, opts ...Option) error

From modifies the error with the given options, if `err` isn't a custom error it then returns a new custom error with the given options.

func GetTemplate added in v1.0.18

func GetTemplate(language, errorType string) (string, error)

GetTemplate retrieves the error template for a specific language and error type. It returns an error if either the language or the template is not found.

func IsCustomError added in v1.2.2

func IsCustomError(err error) bool

IsCustomError checks if the error is a `CustomError`.

func IsErrorCode added in v1.2.3

func IsErrorCode(err error, code string) bool

IsErrorCode checks if the error is a `CustomError` with the specified code.

func IsHTTPStatus added in v1.2.3

func IsHTTPStatus(err error, statusCode int) bool

IsHTTPStatus checks if the error is a `CustomError` with the specified HTTP status code.

func IsRetryable added in v1.2.3

func IsRetryable(err error) bool

IsRetryable checks if the error is a retryable `CustomError`.

func MustAddNewLanguage added in v1.2.6

func MustAddNewLanguage(
	language string,
	errorTypePrefixTemplateMap ErrorPrefixMap,
)

MustAddNewLanguage is a wrapper around AddNewLanguage that panics if an error occurs.

func New

func New(message string, opts ...Option) error

New creates a new validated custom error returning it as en `error`.

Example

Demonstrates how to create static, and dynamic custom errors, also how to check, and instrospect custom errors.

// Custom static error definition.
ErrMissingID := NewMissingError("id", WithErrorCode("E1010"))

// Some function, for demo purpose.
SomeFunc := func(id string) error {
	if id == "" {
		// Usage of the custom static error.
		return ErrMissingID
	}

	// Dynamic custom error.
	return NewFailedToError("write to disk", WithErrorCode("E1523"))
}

// Case: Without `id`, returns `ErrMissingID`.
if err := SomeFunc(""); err != nil {
	fmt.Println(errors.Is(err, ErrMissingID)) // true

	var cE *CustomError
	if errors.As(err, &cE) {
		fmt.Println(cE.StatusCode) // 400
	}

	fmt.Println(err) // E1010: missing id (400 - Bad Request)
}

// Case: With `id`, returns dynamic error.
if err := SomeFunc("12345"); err != nil {
	var cE *CustomError
	if errors.As(err, &cE) {
		fmt.Println(cE.StatusCode) // 500
	}

	fmt.Println(err) // E1523: failed to write to disk (500 - Internal Server Error)
}
Output:

true
400
E1010: missing id
500
E1523: failed to write to disk
Example (I18n)

ExampleNew_i18n demonstrates how to create an error catalog with translations and how to throw errors in different languages.

SEE: `i18n.md` file for more information.

//////
// The following, is usually defined in the `errorcatalog.go` file.
//////

const (
	//////
	// Define the error code constant. It helps to identify the error in
	// systems like Elasticsearch, Splunk and Datadog. It also helps to
	// maintain consistency across the application.
	//////

	ErrInvalidHardDrivePath = "ERR_INVALID_HARD_DRIVE_PATH"
)

// Create the application error catalog.
catalog := MustNewCatalog("myIncredibleApp").
	// Add errors by their constant error codes while setting up the
	// translations. For Spanish and French, it uses the built-in list of
	// common languages instead of hardcoding the language.
	//
	// NOTE: For supported built-in languages, the default word(s) used by
	// the built-in error functions (example: `NewFailedToError`), are
	// automatically translated and included in the message.
	//
	// SEE: `languages.go` file an up-to-date list of the supported built-in
	// list of languages.
	//
	// For ANY other language there are two options:
	// 1. Don't use the built-in functions but instead use the `New` function
	// and write the message in full, for example: "invalid hard drive path".
	// 2. Setup the language (see `ExampleNew_i18nSetupNewLang`).
	//
	// Reason: It's impossible for any package to cover all the possible
	// languages, combinations, and their translations.
	//
	// No need to add the "invalid" word.
	MustSet(ErrInvalidHardDrivePath, "hard drive path",
		// No need to add the "invalid" word.
		WithTranslation(Spanish.String(), "ruta de disco duro"),

		// No need to add the "invalid" word.
		WithTranslation(French.String(), "chemin de disque dur"),
	)

//////
// The following, from anywhere in the application.
//////

// Retrieve the error from the catalog.
err := catalog.MustGet(ErrInvalidHardDrivePath)

// Throw that in the Spanish language as an and using the built-in
// `InvalidError` function which automatically sets the HTTP status code to
// `StatusBadRequest`. The language is specified by using the built-in list
// of languages. The default "invalid" word is automatically translated.
//
// SEE: `languages.go` file for the list of languages.
fmt.Println(err.NewInvalidError(WithLanguage(Spanish.String())))

// The same, but in French.
fmt.Println(err.NewInvalidError(WithLanguage(French.String())))

// The same, standard way - in English.
fmt.Println(err.NewInvalidError())
Output:

ruta de disco duro inválido
chemin de disque dur invalide
invalid hard drive path
Example (I18nSetupNewLang)

ExampleNew_i18n demonstrates how to create an error catalog with translations how to throw errors in different languages, and how to setup a new language.

SEE: `i18n.md` file for more information.

//////
// The following, is usually defined in the `errorcatalog.go` file.
//////

// Define a constant for the new language, to help with consistency.
const Japanase = "jp"

// Let's pretend that beyond English, Spanish, and French, the
// application must support Japanese. In this case, Japanase is not part
// of the built-in list of languages. We start by setting up the language,
// and the respective built-in functions error messages.
//
// NOTE: MustAddNewLanguage properly updates the internal, package-level,
// singleton.
//
// WARN: If you use `MustAddNewLanguage`, and specify an invalid language
// such as "asd", it will panic! The language must be a valid ISO 639-1 or
// ISO 3166-1 alpha-2.
MustAddNewLanguage("jp", NewErrorPrefixMap(
	// "failed to" template.
	"%s に失敗しました",

	// "invalid" template.
	"%s が無効です",

	// "missing" template.
	"%s が見つかりません",

	// "required" template.
	"%s が必要です",

	// "not found" template.
	"%s が見つかりませんでした",
))

const (
	//////
	// Define the error code constant. It helps to identify the error in
	// systems like Elasticsearch, Splunk and Datadog. It also helps to
	// maintain consistency across the application.
	//////

	ErrInvalidHardDrivePath = "ERR_INVALID_HARD_DRIVE_PATH"
)

// Create the application error catalog.
catalog := MustNewCatalog("myIncredibleApp").
	// Add errors by their constant error codes while setting up the
	// translations. For Spanish and French, it uses the built-in list of
	// common languages instead of hardcoding the language.
	//
	// NOTE: For supported built-in languages, the default word(s) used by
	// the built-in error functions (example: `NewFailedToError`), are
	// automatically translated and included in the message.
	//
	// SEE: `languages.go` file an up-to-date list of the supported built-in
	// list of languages.
	//
	// For ANY other language there are two options:
	// 1. Don't use the built-in functions but instead use the `New` function
	// and write the message in full, for example: "invalid hard drive path".
	// 2. Setup the language (see `ExampleNew_i18nSetupNewLang`).
	//
	// Reason: It's impossible for any package to cover all the possible
	// languages, combinations, and their translations.
	//
	// No need to add the "invalid" word.
	MustSet(ErrInvalidHardDrivePath, "hard drive path",
		// No need to add the "invalid" word.
		WithTranslation(Spanish.String(), "ruta de disco duro"),

		// No need to add the "invalid" word.
		WithTranslation(French.String(), "chemin de disque dur"),

		// Added support, no need to add the "invalid" word.
		WithTranslation("jp", "ハードドライブのパス"),
	)

// Retrieve the error from the catalog.
err := catalog.MustGet(ErrInvalidHardDrivePath)

// Throw that in the Spanish language as an and using the built-in
// `InvalidError` function which automatically sets the HTTP status code to
// `StatusBadRequest`. The language is specified by using the built-in list
// of languages. The default "invalid" word is automatically translated.
//
// SEE: `languages.go` file for the list of languages.
fmt.Println(err.NewInvalidError(WithLanguage(Spanish.String())))

// The same, but in French.
fmt.Println(err.NewInvalidError(WithLanguage(French.String())))

// The same, but in Japanese.
fmt.Println(err.NewInvalidError(WithLanguage(Japanase)))

// The same, standard way - in English.
fmt.Println(err.NewInvalidError())
Output:

ruta de disco duro inválido
chemin de disque dur invalide
ハードドライブのパス が無効です
invalid hard drive path
Example (Is)

Demonstrates error chain. `errB` will wrap `errA` and will be considered the same by propagating the chain.

errA := NewMissingError("id")
errB := NewMissingError("name", WithError(errA))

fmt.Println(errors.Is(errB, errA))
Output:

true
Example (MarshalJSON)

Demonstrates JSON marshalling of custom errors.

// New buffer string.
var buf strings.Builder

errA := NewMissingError("id")
errB := NewMissingError("name", WithError(errA))

if err := json.NewEncoder(&buf).Encode(errB); err != nil {
	panic(err)
}

fmt.Println(strings.Contains(buf.String(), `message":"missing name. Original Error: missing id`))
Output:

true
Example (NewFactory)

ExampleNew_NewFactory demonstrates the usage of the NewFactory function.

factory := Factory(
	"id",
	WithFields(map[string]interface{}{
		"test1": "test2",
		"test3": "test4",
	}),
	WithTag("testTag1", "testTag2", "testTag3"),
)

childFactory := factory.NewChildError(
	WithFields(map[string]interface{}{
		"test1": "test2",
		"test5": "test6",
	}),
	WithTag("testTag2", "testTag3", "testTag4"),
)

// Write to a buffer and check the output.
var buf bytes.Buffer

fmt.Fprint(&buf, childFactory.NewMissingError())
fmt.Fprint(&buf, childFactory.NewFailedToError())
fmt.Fprint(&buf, childFactory.NewInvalidError())
fmt.Fprint(&buf, childFactory.NewRequiredError())
fmt.Fprint(&buf, childFactory.NewHTTPError(400))

finalMessage := buf.String()

missing := checkIfStringContainsMany(
	finalMessage,
	"missing id", "failed to id", "invalid id", "missing id", "id required", "bad request",
	"Tags:", "Fields:", "testTag1", "testTag2", "testTag3", "testTag4",
	"test1=test2", "test3=test4", "test5=test6",
)

if len(missing) == 0 {
	fmt.Println(true)
} else {
	fmt.Println(missing)
}
Output:

true
Example (NewHTTPError)

Demonstrates the NewHTTPError custom error.

fmt.Println(NewHTTPError(http.StatusNotFound).(*CustomError).APIError())
fmt.Println(NewHTTPError(http.StatusNotFound).(*CustomError).Error())
fmt.Println(NewHTTPError(http.StatusNotFound))
Output:

not found (404 - Not Found)
not found
not found
Example (NewNoMessage)

Demonstrates errors without message but with status code.

fmt.Println(New("", WithStatusCode(http.StatusAccepted)))
fmt.Println(New("", WithStatusCode(http.StatusAccepted), WithErrorCode("E1010")))
fmt.Println(New("", WithStatusCode(http.StatusAccepted)).(*CustomError).APIError())
fmt.Println(New("", WithStatusCode(http.StatusAccepted), WithErrorCode("E1010")).(*CustomError).APIError())

fmt.Println(New("", WithErrorCode("E1010")))
fmt.Println(New("", WithErrorCode("E1010"), WithStatusCode(http.StatusAccepted)))
fmt.Println(New("", WithErrorCode("E1010")).(*CustomError).APIError())
fmt.Println(New("", WithErrorCode("E1010"), WithStatusCode(http.StatusAccepted)).(*CustomError).APIError())
Output:

Accepted
E1010: Accepted
Accepted (202)
E1010: Accepted (202)
E1010
E1010: Accepted
E1010
E1010: Accepted (202)
Example (NewRetryableError)

ExampleNew_Retryable error and SetRetried function. In this example the go go-resiliency/retrier package will be used, but any other retry package should work as well. Additionally, the example demonstrates how to check if the error is retryable, if it has a specific error code, if it has a specific HTTP status code, and if it is a custom error.

// Create a custom retryable error.
retryableCE := NewFailedToError(
	"write to disk",
	WithErrorCode("E1523"),
	WithRetryable(true),
)

// Initialize a retrier with exponential backoff strategy.
r1 := retrier.New(
	retrier.ConstantBackoff(1, 300*time.Millisecond),
	CustomClassifier{},
)

// Execute the request with retry logic.
if err := r1.Run(func() error {
	// Throw the retryable error.
	return retryableCE
}); err != nil {
	fmt.Println(err)
}

fmt.Println(IsRetryable(retryableCE))
fmt.Println(IsErrorCode(retryableCE, "E1523"))
fmt.Println(IsHTTPStatus(retryableCE, http.StatusInternalServerError))
fmt.Println(IsCustomError(retryableCE))

if toCE, ok := To(retryableCE); ok {
	fmt.Println(toCE.StatusCode)
}
Output:

E1523: failed to write to disk. Retryable: true. Retried: true
true
true
true
true
500
Example (Options)

Demonstrates how to create static, and dynamic custom errors, also how to check, and instrospect custom errors.

fmt.Println(
	NewMissingError("id", WithErrorCode("E1010"), WithStatusCode(http.StatusNotAcceptable), WithError(errors.New("some error"))).(*CustomError).APIError(),
)
Output:

E1010: missing id (406 - Not Acceptable). Original Error: some error
Example (OptionsWithField)

Demonstrates the WithField option.

// Store message in a buf to be checked later.
var buf strings.Builder

fmt.Fprintln(&buf, NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithField("testKey1", "testValue1"),
	WithField("testKey2", "testValue2"),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
))

fmt.Fprintln(&buf, NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithField("testKey1", "testValue1"),
	WithField("testKey2", "testValue2"),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
).(*CustomError).APIError())

fmt.Println(len(checkIfStringContainsMany(
	buf.String(),
	"E1010: missing id",
	"E1010: missing id (406 - Not Acceptable)",
	"Original Error: some error",
	"Tags: test1, test2",
	"Fields:",
	"testKey1=testValue1",
	"testKey2=testValue2",
)) == 0)
Output:

true
Example (OptionsWithFields)

Demonstrates the WithFields option.

// Store message in a buf to be checked later.
var buf strings.Builder

fmt.Fprintln(&buf, NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithFields(map[string]interface{}{
		"testKey1": "testValue1",
		"testKey2": "testValue2",
	}),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
))

fmt.Fprintln(&buf, NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithFields(map[string]interface{}{
		"testKey1": "testValue1",
		"testKey2": "testValue2",
	}),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
).(*CustomError).APIError())

fmt.Println(len(checkIfStringContainsMany(
	buf.String(),
	"E1010: missing id",
	"E1010: missing id (406 - Not Acceptable)",
	"Original Error: some error",
	"Tags: test1, test2",
	"Fields:",
	"testKey1=testValue1",
	"testKey2=testValue2",
)) == 0)
Output:

true
Example (OptionsWithIgnoreIf)

Demonstrates the WithIgnoreFunc option.

fmt.Println(NewMissingError("id", WithIgnoreFunc(func(cE *CustomError) bool {
	return strings.Contains(cE.Message, "id")
})) == nil)
Output:

true
Example (OptionsWithIgnoreString)

Demonstrates the WithIgnoreString option.

fmt.Println(NewMissingError("id", WithIgnoreString("id")) == nil)
fmt.Println(NewMissingError("id", WithError(errors.New("hehehe")), WithIgnoreString("hehehe")) == nil)
fmt.Println(NewMissingError("id", WithIgnoreString("hahaha")) == nil)
Output:

true
true
false
Example (OptionsWithTag)

Demonstrates the WithTag option.

fmt.Println(NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
))

fmt.Println(NewMissingError(
	"id",
	WithTag("test1", "test2"),
	WithErrorCode("E1010"),
	WithStatusCode(http.StatusNotAcceptable),
	WithError(errors.New("some error")),
).(*CustomError).APIError())
Output:

E1010: missing id. Original Error: some error. Tags: test1, test2
E1010: missing id (406 - Not Acceptable). Original Error: some error. Tags: test1, test2

func NewFailedToError

func NewFailedToError(message string, opts ...Option) error

NewFailedToError is the building block for errors usually thrown when some action failed, e.g: "Failed to create host". Default status code is `500`.

NOTE: Status code can be redefined, call `SetStatusCode`.

func NewHTTPError added in v1.0.7

func NewHTTPError(statusCode int, opts ...Option) error

NewHTTPError is the building block for simple HTTP errors, e.g.: Not Found.

func NewInvalidError

func NewInvalidError(message string, opts ...Option) error

NewInvalidError is the building block for errors usually thrown when something fail validation, e.g: "Invalid port". Default status code is `400`.

NOTE: Status code can be redefined, call `SetStatusCode`.

func NewMissingError

func NewMissingError(message string, opts ...Option) error

NewMissingError is the building block for errors usually thrown when required information is missing, e.g: "Missing host". Default status code is `400`.

NOTE: Status code can be redefined, call `SetStatusCode`.

func NewNotFoundError added in v1.0.18

func NewNotFoundError(message string, opts ...Option) error

NewNotFoundError is the building block for errors usually thrown when something is not found, e.g: "Host not found". Default status code is `400`.

NOTE: Status code can be redefined, call `SetStatusCode`.

func NewRequiredError

func NewRequiredError(message string, opts ...Option) error

NewRequiredError is the building block for errors usually thrown when required information is missing, e.g: "Port is required". Default status code is `400`.

NOTE: Status code can be redefined, call `SetStatusCode`.

func Wrap

func Wrap(customError error, errors ...error) error

Wrap `customError` around `errors`.

Types

type Catalog added in v1.0.18

type Catalog struct {
	// CustomErrors are the errors in the catalog.
	ErrorCodeErrorMap ErrorCodeErrorMap `json:"custom_errors"`

	// Name of the catalog, usually, the name of the application.
	Name string `json:"name" validate:"required,gt=3"`
}

Catalog contains a set of errors (customerrors).

func MustNewCatalog added in v1.1.4

func MustNewCatalog(name string) *Catalog

MustNewCatalog creates a new Catalog. If an error occurs, panics.

func NewCatalog added in v1.0.18

func NewCatalog(name string) (*Catalog, error)

NewCatalog creates a new Catalog.

func (*Catalog) Get added in v1.0.18

func (c *Catalog) Get(errorCode string, _ ...Option) (*CustomError, error)

Get returns a custom error from the catalog, if not found, returns an error.

func (*Catalog) MustGet added in v1.0.18

func (c *Catalog) MustGet(errorCode string, opts ...Option) *CustomError

MustGet returns a custom error from the catalog, if not found, panics.

func (*Catalog) MustSet added in v1.0.18

func (c *Catalog) MustSet(errorCode string, defaultMessage string, opts ...Option) *Catalog

MustSet a custom error to the catalog. Use options to set default and common values such as fields, tags, etc. If an error occurs, panics.

func (*Catalog) Set added in v1.0.18

func (c *Catalog) Set(errorCode string, defaultMessage string, opts ...Option) (string, error)

Set a custom error to the catalog. Use options to set default and common values such as fields, tags, etc.

type CustomError

type CustomError struct {
	// Code can be any custom code, e.g.: E1010.
	Code string `json:"code,omitempty" validate:"omitempty,gte=2"`

	// Err optionally wraps the original error.
	Err error `json:"-"`

	// Field enhances the error message with more structured information.
	Fields *sync.Map `json:"fields,omitempty"`

	// Human readable message. Minimum length: 3.
	Message string `json:"message" validate:"required,gte=3"`

	// Message in different languages.
	LanguageMessageMap LanguageMessageMap `json:"languageMessageMap"`

	// LanguageErrorTypeMap is a map of language prefixes to templates such
	// as "missing %s", "%s required", "%s invalid", etc.
	LanguageErrorTypeMap LanguageErrorMap `json:"languageErrorTypeMap"`

	// Retryable indicates if the error is retryable.
	Retryable bool `json:"retryable"`

	// Retried indicates if the error has been retried.
	Retried bool `json:"retried"`

	// StatusCode is a valid HTTP status code, e.g.: 404.
	StatusCode int `json:"-" validate:"omitempty,gte=100,lte=511"`

	// Tags is a SET of tags which helps to categorize the error.
	Tags *Set `json:"tags,omitempty"`
	// contains filtered or unexported fields
}

CustomError is the base block to create custom errors. It provides context - a `Message` to an optional `Err`. Additionally a `Code` - for example "E1010", and `StatusCode` can be provided.

func Copy added in v1.0.18

func Copy(src, target *CustomError) *CustomError

Copy performs a deep copy of the src CustomError into the target CustomError. It ensures that all fields, including nested structures like maps and sets, are properly copied. This function is useful when you need to create a new CustomError instance based on an existing one, while avoiding any shared references to mutable fields.

func Factory added in v1.0.18

func Factory(message string, opts ...Option) *CustomError

Factory creates a validated and pre-defined error to be recalled and thrown later, with or without options. Possible options are: - `NewFailedToError` - `NewInvalidError` - `NewMissingError` - `NewRequiredError` - `NewHTTPError`.

func To added in v1.2.4

func To(err error) (*CustomError, bool)

To converts the error to a `CustomError`.

func (*CustomError) APIError

func (cE *CustomError) APIError() string

APIError is like error plus status code information.

func (*CustomError) Error

func (cE *CustomError) Error() string

Error interface implementation returns the properly formatted error message. It will contain `Code`, `Tags`, `Fields` and any wrapped error.

func (*CustomError) FormatError added in v1.2.4

func (cE *CustomError) FormatError(errorType string, opts ...Option) *CustomError

FormatError formats the error message with the given error type.

func (*CustomError) Is

func (cE *CustomError) Is(err error) bool

Is interface implementation ensures chain continuity. Treats `CustomError` as equivalent to `err`.

SEE https://blog.golang.org/go1.13-errors

func (*CustomError) JustError added in v1.0.16

func (cE *CustomError) JustError() string

JustError returns the error message without any additional information.

func (*CustomError) MarshalJSON added in v1.0.6

func (cE *CustomError) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

SEE https://gist.github.com/thalesfsp/3a1252530750e2370345a2418721ff54

func (*CustomError) New added in v1.0.18

func (cE *CustomError) New(opts ...Option) error

New is the building block for other errors. Preferred method to be used for translations (WithLanguage).

func (*CustomError) NewChildError added in v1.0.14

func (cE *CustomError) NewChildError(opts ...Option) *CustomError

NewChildError creates a new `CustomError` with the same fields and tags of the parent `CustomError` plus the new fields and tags passed as arguments.

func (*CustomError) NewFailedToError added in v1.0.14

func (cE *CustomError) NewFailedToError(opts ...Option) error

NewFailedToError is the building block for errors usually thrown when some action failed, e.g: "Failed to create host". Default status code is `500`.

NOTE: Preferably don't use with the `WithLanguage` because of the "Failed to" part. Prefer to use `New` instead.

NOTE: Status code can be redefined, call `SetStatusCode`.

func (*CustomError) NewHTTPError added in v1.0.14

func (cE *CustomError) NewHTTPError(statusCode int, opts ...Option) error

NewHTTPError is the building block for simple HTTP errors, e.g.: Not Found.

NOTE: `WithLanguage` has no effect on it because of it's just a simple HTTP error.

NOTE: Status code can be redefined, call `SetStatusCode`.

func (*CustomError) NewInvalidError added in v1.0.14

func (cE *CustomError) NewInvalidError(opts ...Option) error

NewInvalidError is the building block for errors usually thrown when something fail validation, e.g: "Invalid port". Default status code is `400`.

NOTE: Preferably don't use with the `WithLanguage` because of the "Invalid" part. Prefer to use `New` instead.

NOTE: Status code can be redefined, call `SetStatusCode`.

func (*CustomError) NewMissingError added in v1.0.14

func (cE *CustomError) NewMissingError(opts ...Option) error

NewMissingError is the building block for errors usually thrown when required information is missing, e.g: "Missing host". Default status code is `400`.

NOTE: Preferably don't use with the `WithLanguage` because of the "Missing" part. Prefer to use `New` instead.

NOTE: Status code can be redefined, call `SetStatusCode`.

func (*CustomError) NewRequiredError added in v1.0.14

func (cE *CustomError) NewRequiredError(opts ...Option) error

NewRequiredError is the building block for errors usually thrown when required information is missing, e.g: "Port is required". Default status code is `400`.

NOTE: Preferably don't use with the `WithLanguage` because of the "Required" part. Prefer to use `New` instead.

NOTE: Status code can be redefined, call `SetStatusCode`.

func (*CustomError) SetMessage added in v1.0.18

func (cE *CustomError) SetMessage(message string)

SetMessage sets the message of the error.

func (*CustomError) SetRetried added in v1.2.2

func (cE *CustomError) SetRetried(status bool)

SetRetried sets if the error has been retried.

func (*CustomError) Unwrap

func (cE *CustomError) Unwrap() error

Unwrap interface implementation returns inner error.

type ErrorCode added in v1.0.18

type ErrorCode string

ErrorCode is the consistent way to express an error. Despite there's no enforcement, it's recommended that to be meanginful, all upper cased and separated by underscore, example: "INVALID_REQUEST".

func NewErrorCode added in v1.0.18

func NewErrorCode(name string) (ErrorCode, error)

NewErrorCode creates a new ErrorCode. It will be validated and stored upper cased.

func (ErrorCode) String added in v1.0.18

func (e ErrorCode) String() string

String implements the Stringer interface.

func (ErrorCode) Validate added in v1.0.18

func (e ErrorCode) Validate() error

Validate if error code follows the pattern.

type ErrorCodeErrorMap added in v1.0.18

type ErrorCodeErrorMap = *sync.Map

ErrorCodeErrorMap is a map of error codes to custom errors.

type ErrorPrefixMap added in v1.0.18

type ErrorPrefixMap = *sync.Map

ErrorPrefixMap is a thread-safe map storing error prefix templates for different error types.

func NewErrorPrefixMap added in v1.0.18

func NewErrorPrefixMap(
	failedToTemplate,
	invalidTemplate,
	missingTemplate,
	requiredTemplate,
	notFoundTemplate string,
) ErrorPrefixMap

NewErrorPrefixMap creates a new ErrorPrefixMap with the provided templates for each error type. This function is useful when setting up custom error templates for a new language.

type ErrorType added in v1.0.18

type ErrorType string

ErrorType represents the category or nature of an error.

const (
	FailedTo ErrorType = "failed to" // Indicates a failure to perform an action
	Invalid  ErrorType = "invalid"   // Denotes an invalid input or state
	Missing  ErrorType = "missing"   // Signifies a missing required element
	NotFound ErrorType = "not found" // Indicates that a requested resource was not found
	Required ErrorType = "required"  // Denotes a required element that was not provided
)

These are the built-in languages supported by the error handling system, and their respective default error message.

func (ErrorType) String added in v1.0.18

func (e ErrorType) String() string

String implements the Stringer interface for ErrorType.

type Language added in v1.0.18

type Language string

Language is a language code.

const (
	Chinese    Language = "ch"
	English    Language = "en"
	French     Language = "fr"
	German     Language = "de"
	Italian    Language = "it"
	Portuguese Language = "pt"
	Spanish    Language = "es"
)

func NewLanguage added in v1.0.18

func NewLanguage(lang string) (Language, error)

NewLanguage creates a new Lang.

func (Language) GetRoot added in v1.0.18

func (l Language) GetRoot() string

GetRoot returns the root language code. Given "en-US", it returns "en".

func (Language) String added in v1.0.18

func (l Language) String() string

String implements the Stringer interface.

func (Language) Validate added in v1.0.18

func (l Language) Validate() error

Validate if lang follows the ISO 639-1 and ISO 3166-1 alpha-2 standard.

type LanguageErrorMap added in v1.0.18

type LanguageErrorMap = *sync.Map

LanguageErrorMap is a thread-safe map storing error templates for different languages.

func GetLanguageErrorMap added in v1.0.18

func GetLanguageErrorMap() LanguageErrorMap

GetLanguageErrorMap returns the singleton instance of LanguageErrorMap. It initializes the map with built-in languages and their respective error templates.

func GetLanguageErrorTypeMap added in v1.0.18

func GetLanguageErrorTypeMap(language string) (LanguageErrorMap, error)

GetLanguageErrorTypeMap retrieves the ErrorPrefixMap for a specific language. It returns an error if the language is not supported.

type LanguageMessageMap added in v1.0.18

type LanguageMessageMap = *sync.Map

LanguageMessageMap is a map of language codes to error messages.

type Option

type Option func(s *CustomError)

Option allows to define error options.

func WithError

func WithError(err error) Option

WithError allows to specify an error which will be wrapped by the custom error.

func WithErrorCode added in v1.2.0

func WithErrorCode(code string) Option

WithErrorCode allows to specify an error code, such as "E1010".

func WithField added in v1.0.13

func WithField(key string, value any) Option

WithField allows to set a field for the error.

func WithFields added in v1.0.15

func WithFields(fields map[string]interface{}) Option

WithFields allows to set fields for the error.

func WithIgnoreFunc added in v1.0.8

func WithIgnoreFunc(f func(cE *CustomError) bool) Option

WithIgnoreFunc ignores an error if the specified function returns true.

func WithIgnoreString added in v1.0.8

func WithIgnoreString(s ...string) Option

WithIgnoreString ignores an error if the error message, or the the underlying error message contains the specified string.

func WithLanguage added in v1.0.18

func WithLanguage(lang string) Option

WithLanguage specifies the language for the error message. It requires `lang` to be a valid ISO 639-1 and ISO 3166-1 alpha-2 standard, and the `LanguageMessageMap` map to be set, otherwise it will be ignored returning the default message. If a language is specified in the "en-US" format, and not found, it will try to find by the root "en".

func WithMessage added in v1.0.18

func WithMessage(msg string) Option

WithMessage allows to specify the error message.

func WithRetryable added in v1.2.2

func WithRetryable(status bool) Option

WithRetryable allows to specify if the error is retryable.

func WithStatusCode

func WithStatusCode(statusCode int) Option

WithStatusCode allows to specify the status code, such as "200".

func WithTag added in v1.0.11

func WithTag(tag ...string) Option

WithTag allows to specify tags for the error.

func WithTranslation added in v1.0.18

func WithTranslation(lang, message string) Option

WithTranslation sets the translation for the error message.

NOTE: For supported built-in languages, the default word(s) used by the built-in error functions (example: `NewFailedToError`), are automatically translated and included in the message.

SEE: `languages.go` file an up-to-date list of the supported built-in list of languages.

For ANY other language there are two options: 1. Don't use the built-in functions but instead use the `New` function and write the message in full, for example: "invalid hard drive path". 2. Setup the language (see `ExampleNew_i18nSetupNewLang`).

Reason: It's impossible for any package to cover all the possible languages, combinations, and their translations.

SEE: `i18n.md` file for more information.

type Set added in v1.0.18

type Set struct {
	*treeset.Set
}

Set is a wrapper around the treeset.Set, providing a collection that stores unique elements in a sorted order. It is used in the CustomError struct to maintain a sorted set of tags.

func (*Set) String added in v1.0.18

func (s *Set) String() string

String implements the Stringer interface for the Set type. It returns a comma-separated string representation of all elements in the set, useful for debugging and error message formatting.

Jump to

Keyboard shortcuts

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