problem

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 13, 2024 License: MIT Imports: 18 Imported by: 0

README

go-problem

Go Reference Build Status Release License

Flexible and customizable Go (golang) implementation of RFC 9457; Problem Details for HTTP APIs.

Installation

Install using go install:

go install github.com/neocotic/go-problem

Then import the package into your own code:

import "github.com/neocotic/go-problem"

Documentation

Documentation is available on pkg.go.dev. It contains an overview and reference.

Example

Define reusable problem types and/or definitions:

var (
	NotFound = problem.Type{
		LogLevel: problem.LogLevelDebug,
		Status:   http.StatusNotFound,
		Title:    http.StatusText(http.StatusNotFound),
	}
	NotFoundDefinition = problem.Definition{
		Type: NotFound,
	}
)

Create a problem using the builder pattern:

problem.Build().
	Definition(NotFoundDefinition).
	Code(problem.MustBuildCode(404, "USER")).
	Detail("User not found").
	Instance("https://api.example.void/users/123").
	Wrap(err).
	Problem()

Or the option pattern:

problem.New(
	problem.FromDefinition(NotFoundDefinition),
	problem.WithCode(problem.MustBuildCode(404, "USER")),
	problem.WithDetail("User not found"),
	problem.WithInstance("https://api.example.void/users/123"),
	problem.Wrap(err),
)

There's a load of other functions to explore and problem.Generator can be used to customize the experience much further. It even comes with functions for writing problems and errors to an HTTP response and middleware for panic recovery with problem responses.

Issues

If you have any problems or would like to see changes currently in development you can do so here.

Contributors

If you want to contribute, you're a legend! Information on how you can do so can be found in CONTRIBUTING.md. We want your suggestions and pull requests!

A list of contributors can be found in AUTHORS.md.

License

Copyright © 2024 neocotic

See LICENSE.md for more information on our MIT license.

Documentation

Overview

Package problem provides support for generating "problem details" in accordance to RFC 9457 https://datatracker.ietf.org/doc/html/rfc9457, represented as a Problem. A Generator can be used to control a lot of the logic applied when generating problems. When not specified, DefaultGenerator, the zero value of Generator, is used where appropriate.

While a Problem can be created manually by populating fields, this is not the intended approach will result in many of the controls offered by Generator being lost. Instead a Problem is constructed using either Build or New with options. Both are the same as the latter uses a Builder under-the-hood, so the only real difference is syntax.

A Problem is an error and, as such, can wrap another error and can be wrapped. Wrap (and Builder.Wrap) can be used for wrapped while As and Is can be used for unwrapping.

The package also provides opt-in support for stack trace capturing and UUID generation for problems along with the concept of a problem Code.

Index

Constants

View Source
const (
	// DefaultLogArgKey is the default argument key passed to Logger immediately before the Problem at the end of the
	// arguments, and is used by DefaultGenerator.
	DefaultLogArgKey = "problem"

	// DefaultLogLevel is the LogLevel used when one could not be derived.
	DefaultLogLevel = LogLevelError
)
View Source
const (
	// ContentTypeJSON is the recommended content/media type to represent a problem in JSON format.
	ContentTypeJSON = "application/problem+json"
	// ContentTypeJSONUTF8 is the recommended content/media type to represent a problem in JSON format with UTF-8
	// encoding.
	ContentTypeJSONUTF8 = ContentTypeJSON + "; charset=utf-8"
	// ContentTypeXML is the recommended content/media type to represent a Problem in XML format.
	ContentTypeXML = "application/problem+xml"
	// ContentTypeXMLUTF8 is the recommended content/media type to represent a problem in XML format with UTF-8
	// encoding.
	ContentTypeXMLUTF8 = ContentTypeXML + "; charset=utf-8"

	// DefaultTypeURI is the default problem type URI, indicating that a problem has no additional semantics beyond that
	// its status.
	//
	// Typically, when used, the problem title SHOULD be the same as the recommended HTTP status text for that code
	// (e.g. "Not Found" for 404).
	DefaultTypeURI = "about:blank"
)
View Source
const DefaultCodeSeparator rune = '-'

DefaultCodeSeparator is the default rune used to separate the NS and value of a Code and is used by DefaultGenerator.

View Source
const (
	// DefaultTitle is the title given to a Problem if one was not explicitly specified or could be derived.
	DefaultTitle = "Unknown Error"
)

Variables

View Source
var DefaultGenerator = &Generator{}

DefaultGenerator is the default Generator used when none is given to some top-level functions and structs.

While relatively unopinionated, it is designed to work out-of-the-box with the most commonly desired behaviour having the following characteristics:

  • Stack traces are not captured and UUIDs are not generated by default (see Generator.StackFlag and Generator.UUIDFlag respectively for more information)
  • Any UUID that is generated (e.g. via Builder.UUID or WithUUID) is a (V4) UUID (see Generator.UUIDGenerator for more information)
  • Any stack trace, UUID, or LogLevel of a Problem found in the tree of an error passed to Builder.Wrap or Wrap is unwrapped and treated as defaults for the generated Problem by default (see Generator.Unwrapper for more information)
  • Any translation keys are ignored (see Generator.Translator for more information)
  • Any Code constructed and/or parsed can have any non-empty NS and value and are separated by DefaultCodeSeparator (see Generator.CodeNSValidator, Generator.CodeValueLen, and Generator.CodeSeparator respectively for more information)
  • Any message that is logged (e.g. via Generator.Log or Generator.LogContext) is done so using slog.Default with DefaultLogArgKey passed as the key along with a Problem within the last two arguments (see Generator.Logger and Generator.LogArgKey respectively for more information)
  • The LogLevel derived from a Type is always Type.LogLevel (see Generator.LogLeveler for more information)
View Source
var ErrCode = errors.New("invalid problem code")

ErrCode is returned when a Code cannot be constructed or parsed.

Functions

func Is

func Is(err error) bool

Is acts as a substitute for errors.Is, returning true if err's tree contains a Problem.

It is effectively a convenient shorthand for calling As where only the boolean return value is returned.

func IsMatch

func IsMatch(err error, matchers ...Matcher) bool

IsMatch acts as a substitute for errors.Is, returning true if err's tree contains a Problem that matches all matchers provided.

It is effectively a convenient shorthand for calling AsMatch where only the boolean return value is returned.

func Log

func Log(msg string, prob *Problem, args ...any)

Log is a convenient shorthand for calling Generator.Log on DefaultGenerator.

func LogContext

func LogContext(ctx context.Context, msg string, prob *Problem, args ...any)

LogContext is a convenient shorthand for calling Generator.LogContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator.

func Match

func Match(prob *Problem, matchers ...Matcher) bool

Match returns whether the given Problem matchers all the matchers provided.

If one or more Matcher is provided but prob is nil, false will always be returned as a Matcher assumes prob is not nil.

func Middleware

func Middleware(probFunc func(err error) *Problem, opts ...WriteOptions) func(http.Handler) http.Handler

Middleware is a convenient shorthand for calling MiddlewareUsing with DefaultGenerator.

func MiddlewareUsing

func MiddlewareUsing(gen *Generator, probFunc func(err error) *Problem, opts ...WriteOptions) func(http.Handler) http.Handler

MiddlewareUsing returns a middleware function that is responsible for populating the HTTP request's context.Context with the given Generator (which can be retrieved using GetGenerator) and also provides panic recovery, allowing recovered values to be used to form Problem HTTP responses, optionally using WriteOptions for more granular control.

If a value recovered from a panic is not a Problem (which is highly likely), probFunc is called with an error representation of that value (if not already an error) to be used to construct a Problem.

func MustValidateCode

func MustValidateCode(code Code, ns ...NS)

MustValidateCode is a convenient shorthand for calling Coder.MustValidate on a Coder using DefaultGenerator and optionally a given NS.

func UsingGenerator

func UsingGenerator(parent context.Context, gen *Generator) context.Context

UsingGenerator returns a copy of the given parent context.Context containing the Generator provided.

If gen is nil, DefaultGenerator is used.

func ValidateCode

func ValidateCode(code Code, ns ...NS) error

ValidateCode is a convenient shorthand for calling Coder.Validate on a Coder using DefaultGenerator and optionally a given NS.

func WriteError

func WriteError(err error, w http.ResponseWriter, req *http.Request, fn func(err error) *Problem, opts ...WriteOptions) error

WriteError is a convenient shorthand for calling Generator.WriteError on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

func WriteErrorJSON

func WriteErrorJSON(err error, w http.ResponseWriter, req *http.Request, fn func(err error) *Problem, opts ...WriteOptions) error

WriteErrorJSON is a convenient shorthand for calling Generator.WriteErrorJSON on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

func WriteErrorXML

func WriteErrorXML(err error, w http.ResponseWriter, req *http.Request, fn func(err error) *Problem, opts ...WriteOptions) error

WriteErrorXML is a convenient shorthand for calling Generator.WriteErrorXML on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

func WriteProblem

func WriteProblem(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblem is a convenient shorthand for calling Generator.WriteProblem on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

func WriteProblemJSON

func WriteProblemJSON(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblemJSON is a convenient shorthand for calling Generator.WriteProblemJSON on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

func WriteProblemXML

func WriteProblemXML(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblemXML is a convenient shorthand for calling Generator.WriteProblemXML on the Generator within the given HTTP request's context.Context, if any, otherwise DefaultGenerator.

Types

type Builder

type Builder struct {
	// Generator is the Generator to be used when building a Problem.
	//
	// If Generator is nil, DefaultGenerator will be used.
	Generator *Generator
	// contains filtered or unexported fields
}

Builder is used to construct a Problem using methods to define fields and/or override fields derived from a Definition and/or Type.

func Build

func Build() Builder

Build is a convenient shorthand for calling Generator.Build on DefaultGenerator.

func BuildContext

func BuildContext(ctx context.Context) Builder

BuildContext is a convenient shorthand for calling Generator.BuildContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator.

func (*Builder) Clone

func (b *Builder) Clone() *Builder

Clone returns a clone of the Builder.

func (*Builder) Code

func (b *Builder) Code(code Code) *Builder

Code sets the given Code to be used when building a Problem. See Problem.Code for more information.

If code is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap.

func (*Builder) Definition

func (b *Builder) Definition(def Definition) *Builder

Definition sets the given Definition to be used when building a Problem.

The fields of def are treated as defaults when a field is not explicitly defined. This method can conflict with Builder.DefinitionType as it effectively assigns to the same underlying field.

func (*Builder) DefinitionType

func (b *Builder) DefinitionType(defType Type) *Builder

DefinitionType sets the given Type to be used when building a Problem.

The fields of defType are treated as defaults when a field is not explicitly defined. This method can conflict with Builder.Definition as it effectively assigns to the same underlying field, however, only setting Definition.Type.

func (*Builder) Detail

func (b *Builder) Detail(detail string) *Builder

Detail sets the given detail to be used when building a Problem. See Problem.Detail for more information.

If detail is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap. However, if a localized detail is resolved from a translation key using Builder.DetailKey, that will take precedence over detail.

func (*Builder) DetailKey

func (b *Builder) DetailKey(key any) *Builder

DetailKey sets the translation key for to be used to localize the detail when building a Problem. See Problem.Detail for more information.

The localized detail will be looked up using Generator.Translator, where possible. If resolved, it will take precedence over anything provided using Builder.Detail, Builder.Definition, or Builder.Wrap.

func (*Builder) Detailf

func (b *Builder) Detailf(format string, args ...any) *Builder

Detailf sets the given formatted detail to be used when building a Problem. See Problem.Detail for more information.

If the formatted detail is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap. However, if a localized detail is resolved from a translation key using Builder.DetailKey, that will take precedence over the formatted detail.

func (*Builder) Extension

func (b *Builder) Extension(key string, value any) *Builder

Extension appends the given extension key and value to that used when building a Problem. See Problem.Extensions for more information.

When used, it will take precedence over any extensions provided using Builder.Definition or Builder.Wrap.

Panics if key is either empty or reserved (i.e. conflicts with Problem-level fields).

Builder.Extensions may be preferred for providing multiple extensions and does not conflict with usage of Extension in that neither method will delete/modify extensions unless the key overlaps, in which case the value will be overwritten.

func (*Builder) Extensions

func (b *Builder) Extensions(extensions Extensions) *Builder

Extensions sets a shallow clone of the given extensions to be used when building a Problem. See Problem.Extensions for more information.

If extensions is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap.

Panics if extensions contains a key that is either empty or reserved (i.e. conflicts with Problem-level fields).

Builder.Extension may be preferred for providing a single extension and does not conflict with usage of Extensions in that neither method will delete/modify extensions unless the key overlaps, in which case the value will be overwritten.

func (*Builder) Instance

func (b *Builder) Instance(instanceURI string) *Builder

Instance sets the instance URI reference to be used when building a Problem. See Problem.Instance for more information.

An uri.Builder can be used to aid building the URI reference.

If instanceURI is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap.

func (*Builder) Instancef

func (b *Builder) Instancef(format string, args ...any) *Builder

Instancef sets the formatted instance URI reference to be used when building a Problem. See Problem.Instance for more information.

If the formatted instance URI reference is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap.

func (*Builder) LogLevel

func (b *Builder) LogLevel(level LogLevel) *Builder

LogLevel sets the LogLevel to be used when building a Problem. See Problem.LogLevel for more information.

If level is not zero, it will take precedence over anything provided using Builder.Definition, Builder.DefinitionType, or Builder.Wrap.

func (*Builder) Problem

func (b *Builder) Problem() *Problem

Problem returns a constructed Problem.

func (*Builder) Reset

func (b *Builder) Reset() *Builder

Reset clears all information used to build a Problem.

func (*Builder) Stack

func (b *Builder) Stack(flags ...Flag) *Builder

Stack sets the flags to be used to control if/how a captured stack trace is visible when building a Problem. See Problem.Stack for more information.

By default, Generator.StackFlag is used to control visibility of a stack trace.

If no flags are provided, this is considered equal to passing FlagField and FlagLog. If FlagDisable is given, all other flags are ignored. No stack trace is captured if FlagDisable is provided.

If a stack trace needs to be captured and Builder.Wrap is used and a Problem is unwrapped that already has a stack trace, its stack trace will be used instead of recapturing a stack trace that could potentially result in loss of depth in terms of stack frames.

func (*Builder) StackFramesSkipped

func (b *Builder) StackFramesSkipped(skipped int) *Builder

StackFramesSkipped sets the number of additional frames to be skipped if/when a stack trace is captured. See Builder.Stack and Problem.Stack for more information.

If skipped is less than or equal to zero, no additional frames will be skipped.

func (*Builder) Status

func (b *Builder) Status(status int) *Builder

Status sets the given status to be used when building a Problem. See Problem.Status for more information.

If status is not zero, it will take precedence over anything provided using Builder.Definition, Builder.DefinitionType, or Builder.Wrap.

func (*Builder) String

func (b *Builder) String() string

String constructs a Problem and returns its string representation.

func (*Builder) Title

func (b *Builder) Title(title string) *Builder

Title sets the given title to be used when building a Problem. See Problem.Title for more information.

If title is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap. However, if a localized title is resolved from a translation key using Builder.TitleKey, that will take precedence over title.

func (*Builder) TitleKey

func (b *Builder) TitleKey(key any) *Builder

TitleKey sets the translation key for to be used to localize the title when building a Problem. See Problem.Title for more information.

The localized title will be looked up using Generator.Translator, where possible. If resolved, it will take precedence over anything provided using Builder.Title, Builder.Definition, or Builder.Wrap.

func (*Builder) Titlef

func (b *Builder) Titlef(format string, args ...any) *Builder

Titlef sets the given formatted title to be used when building a Problem. See Problem.Title for more information.

If the formatted title is not empty, it will take precedence over anything provided using Builder.Definition or Builder.Wrap. However, if a localized title is resolved from a translation key using Builder.TitleKey, that will take precedence over the formatted title.

func (*Builder) Type

func (b *Builder) Type(typeURI string) *Builder

Type sets the type URI reference to be used when building a Problem. See Problem.Type for more information.

An uri.Builder can be used to aid building the URI reference.

If typeURI is not empty, it will take precedence over anything provided using Builder.Definition, Builder.DefinitionType, or Builder.Wrap.

func (*Builder) Typef

func (b *Builder) Typef(format string, args ...any) *Builder

Typef sets the formatted type URI reference to be used when building a Problem. See Problem.Type for more information.

If the formatted type URI reference is not empty, it will take precedence over anything provided using Builder.Definition, Builder.DefinitionType, or Builder.Wrap.

func (*Builder) UUID

func (b *Builder) UUID(flags ...Flag) *Builder

UUID sets the flags to be used to control if/how a generated "UUID" is visible when building a Problem. See Problem.UUID for more information.

By default, Generator.UUIDFlag is used to control visibility of a "UUID".

If no flags are provided, this is considered equal to passing FlagField and FlagLog. If FlagDisable is given, all other flags are ignored. No "UUID" is generated if FlagDisable is provided.

If a "UUID" needs to be generated and Builder.Wrap is used and a Problem is unwrapped that already has a "UUID", its "UUID" will be used instead of generating a "UUID" to ensure that it is used consistently once generated to make tracing any logs, specifically, a lot easier.

func (*Builder) Wrap

func (b *Builder) Wrap(err error, unwrapper ...Unwrapper) *Builder

Wrap sets the error to be wrapped when building a Problem. See Problem.Error and Problem.Unwrap for more information.

Additionally, more control can be achieved over the scenario where err's tree contains a Problem by passing an Unwrapper; a function responsible for deciding what, if any, information from a wrapped Problem is to be used when building a Problem. Any such information will not take precedence over any explicitly defined Problem fields, however, it will take precedence over any information derived from a Definition or its Type using Builder.Definition and/or Builder.DefinitionType.

If no Unwrapper is provided, Generator.Unwrapper is used from Builder.Generator if not nil, otherwise from DefaultGenerator. If an Unwrapper could still not be resolved, it defaults to PropagatedFieldUnwrapper.

type Code

type Code string

Code is intended to be a unique string that identifies a specific occurrence of a Problem.

Consumers can use a Code to communicate with the owner of the generator to help debug a problem and the client can use it to handle a specific occurrence of a problem without risk of conflicts with other problems using the same status, title, and/or type URI reference.

A well-formed Code is expected to be formed of a namespace (NS) and a numeric value, separated using Generator.CodeSeparator.

While Code is not strictly associated with problems, it is common for such a concept to be used in combination and so is provided as an optional extra.

Since Code is just a string, there's nothing to prevent a Code being explicitly declared, for example;

code := Code("AUTH-400")

However, this is discouraged in favour of using Coder to construct consistent codes that conform to validation defined on a Generator. That said; it really just depends on how important Code consistency is to the generator.

func BuildCode

func BuildCode(value uint, ns ...NS) (Code, error)

BuildCode is a convenient shorthand for calling Coder.Build on a Coder using DefaultGenerator and optionally a given NS.

func MustBuildCode

func MustBuildCode(value uint, ns ...NS) Code

MustBuildCode is a convenient shorthand for calling Coder.MustBuild on a Coder using DefaultGenerator and optionally a given NS.

type Coder

type Coder struct {
	// Generator is the Generator to be used when building/parsing a Code.
	//
	// If Generator is nil, DefaultGenerator will be used.
	Generator *Generator
	// NS is the namespace to be used when building/parsing a Code. It is only required when building a Code but,
	// when present when parsing a Code, it also validates that the parsed Code was constructed using the same NS.
	NS NS
}

Coder is used to construct and/or parse a Code.

Since certain fields of Generator are used by a Coder to construct/parse a Code, the same Generator should be used for both operations to ensure consistency and validity of each Code.

func (Coder) Build

func (c Coder) Build(value uint) (Code, error)

Build returns a constructed Code using the given value, if able.

Coder.NS is required as it's also used during the construction and is separated from value using Generator.CodeSeparator.

An ErrCode is returned only in the following cases:

  • Generator.CodeSeparator is a non-printable rune
  • Coder.ValidateNS rejects Coder.NS
  • Coder.ValidateValue rejects value

func (Coder) MustBuild

func (c Coder) MustBuild(value uint) Code

MustBuild is a convenient shorthand for calling Coder.Build that panics if it returns an error.

func (Coder) MustParse

func (c Coder) MustParse(code Code) ParsedCode

MustParse is a convenient shorthand for calling Coder.Parse that panics if it returns an error.

func (Coder) MustValidate

func (c Coder) MustValidate(code Code)

MustValidate is a convenient shorthand for calling Coder.Validate that panics if it returns an error.

func (Coder) Parse

func (c Coder) Parse(code Code) (ParsedCode, error)

Parse parses the given Code, if able.

Coder.NS is optional but, where present, will result in additional validation on the parsed NS, asserting that they are equal. Generator.CodeSeparator is used during parsing to separate the NS and value of the Code.

The returned ParsedCode will contain as much information parsed for code as possible, even though it may be invalid. Only when the returned error is nil can it be assumed that ParsedCode contains only valid information.

An ErrCode is returned only in the following cases:

  • Generator.CodeSeparator is a non-printable rune
  • Coder.ValidateNS rejects the parsed NS
  • Coder.ValidateValue rejects the parsed value, or it cannot be parsed as an uint

func (Coder) Validate

func (c Coder) Validate(code Code) error

Validate validates the given Code and returns an ErrCode if invalid.

It is effectively a convenient shorthand for calling Coder.Parse where only the error is returned.

An ErrCode is returned only in the following cases:

  • Generator.CodeSeparator is a non-printable rune
  • Coder.ValidateNS rejects the parsed NS
  • Coder.ValidateValue rejects the parsed value, or it cannot be parsed as an uint

func (Coder) ValidateNS

func (c Coder) ValidateNS(ns NS) error

ValidateNS validates the given NS and returns an ErrCode if invalid.

An ErrCode is returned only in the following cases:

  • Generator.CodeSeparator is a non-printable rune
  • ns is empty
  • ns contains Generator.CodeSeparator
  • Generator.CodeNSValidator rejects ns, if not nil

func (Coder) ValidateValue

func (c Coder) ValidateValue(value uint) error

ValidateValue validates the given value and returns an ErrCode if invalid.

An ErrCode is returned only if number of digits in the string representation of is greater than Generator.CodeValueLen, if greater than zero.

type Definition

type Definition struct {
	// Code is the default code to be assigned to a Problem generated from the Definition. See Problem.Code for more
	// information.
	//
	// If Code is empty, no default is used.
	Code Code `json:"code" xml:"code" yaml:"code"`
	// Detail is the default detail to be assigned to a Problem generated from the Definition. See Problem.Detail for
	// more information.
	//
	// If Detail is empty, no default is used.
	//
	// If DetailKey is not empty and can be resolved it will take precedence over Detail.
	Detail string `json:"detail" xml:"detail" yaml:"detail"`
	// DetailKey is the translation key of the default detail to be assigned to a Problem generated from the Definition.
	// See Problem.Detail for more information.
	//
	// The localized detail will be looked up using Generator.Translator, where possible. If resolved, it will take
	// precedence over Detail.
	//
	// If DetailKey is empty it, it is ignored.
	DetailKey any `json:"detailKey" xml:"detailKey" yaml:"detailKey"`
	// Extensions is the default extensions to be assigned to a Problem generated from the Definition. See
	// Problem.Extensions for more information.
	//
	// If Extensions is nil, no default is used.
	Extensions map[string]any `json:"extensions" xml:"extensions" yaml:"extensions"`
	// Instance is the default instance URI to be assigned to a Problem generated from the Definition. See
	// Problem.Instance for more information.
	//
	// An uri.Builder can be used to aid building the URI reference.
	//
	// If Instance is empty, no default is used.
	Instance string `json:"instance" xml:"instance" yaml:"instance"`
	// Type contains fields defining the type of Problem generated from the Definition, typically containing additional
	// default values.
	Type Type `json:"type" xml:"type" yaml:"type"`
}

Definition represents a reusable definition of problem occurrence that may contain default values that can be used when generating a Problem from a specific definition.

A Definition provides an alternative approach to generating problems where they can be used to dictate all information populated within a Problem and/or combined with options to provide more granular control and overrides.

func (Definition) Build

func (d Definition) Build() Builder

Build is a convenient shorthand for calling Generator.Build on DefaultGenerator with the Definition already passed to Builder.Definition.

func (Definition) BuildContext

func (d Definition) BuildContext(ctx context.Context) Builder

BuildContext is a convenient shorthand for calling Generator.BuildContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator, with the Definition already passed to Builder.Definition.

func (Definition) BuildContextUsing

func (d Definition) BuildContextUsing(ctx context.Context, gen *Generator) Builder

BuildContextUsing is a convenient shorthand for calling Generator.BuildContext with the Definition already passed to Builder.Definition.

func (Definition) BuildUsing

func (d Definition) BuildUsing(gen *Generator) Builder

BuildUsing is a convenient shorthand for calling Generator.Build with the Definition already passed to Builder.Definition.

func (Definition) New

func (d Definition) New(opts ...Option) *Problem

New is a convenient shorthand for calling Generator.New on DefaultGenerator, including FromDefinition with the Definition along with any specified options.

func (Definition) NewContext

func (d Definition) NewContext(ctx context.Context, opts ...Option) *Problem

NewContext is a convenient shorthand for calling Generator.NewContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator, including FromDefinition with the Definition along with any specified options.

func (Definition) NewContextUsing

func (d Definition) NewContextUsing(ctx context.Context, gen *Generator, opts ...Option) *Problem

NewContextUsing is an alternative for calling Generator.NewContext, including FromDefinition with the Definition along with any specified options.

func (Definition) NewUsing

func (d Definition) NewUsing(gen *Generator, opts ...Option) *Problem

NewUsing is an alternative for calling Generator.New, including FromDefinition with the Definition along with any specified options.

type Extensions

type Extensions map[string]any

Extensions is a map that may contain additional information used extend the details of a Problem.

func (Extensions) MarshalXML

func (es Extensions) MarshalXML(e *xml.Encoder, _ xml.StartElement) error

MarshalXML marshals the encoded entries within the map into XML.

This is required in order to allow extensions to be marshaled at the top-level of a Problem. Additionally, xml.Unmarshaler does not support marshaling maps by default since XML documents are ordered by design. Unfortunately, this function cannot guarantee ordering.

An error is returned if unable to marshal any of the entries or the map contains a key that is either empty or reserved (i.e. conflicts with Problem-level fields).

func (Extensions) UnmarshalXML

func (es Extensions) UnmarshalXML(_ *xml.Decoder, _ xml.StartElement) error

UnmarshalXML does nothing but is required to ensure that any <extensions> element is not unmarshaled as this cannot be supported.

type Flag

type Flag uint8

Flag provides control over the generation of specific data and its visibility on their respective fields on a Problem.

A Generator will a field corresponding to each support Flag use case that contains the default Flag.

If a Flag-supporting method on a Builder is called or a Flag-supporting Option is used, but no flags are provided, this is considered equal to passing FlagField and FlagLog. This would mean that the corresponding data will be generated and the fully visible on the Problem both in terms of field and within the logs. If FlagDisable is ever passed, all other flags are ignored and the corresponding data is not generated (or inherited) and will not be visible on the Problem.

const (
	// FlagField triggers generation (or inheritance) of the corresponding data and populates the respective exported
	// field on the Problem.
	//
	// Effectively, if the Problem is accessed either directly or deserialized, the data will be accessible, however,
	// FlagField alone will not result in the data being present any time the Problem is logged. FlagLog is required for
	// that.
	FlagField Flag = 1 << iota
	// FlagLog triggers generation (or inheritance) of the corresponding data and populates the respective unexported
	// field on the Problem.
	//
	// Effectively, any time the Problem is logged the data will be present, however, if the Problem is accessed either
	// directly or deserialized the data will be inaccessible unless used in combination with FlagField.
	FlagLog
)
const FlagDisable Flag = 0

FlagDisable disables generation (or inheritance) of the corresponding data and hides it on the Problem.

type Generator

type Generator struct {
	// CodeNSValidator is the NSValidator used to perform additional validation on a NS used within a Code constructed
	// and/or parsed by a Coder.
	//
	// When nil, a Code may contain any rune except Generator.CodeSeparator within its NS so long as it is not empty.
	// Even when not nil, the NS of a Code must not contain Generator.CodeSeparator and cannot be empty.
	//
	// For example;
	//
	//	g := &Generator{CodeNSValidator: ComposeNSValidator(
	//		LenNSValidator(1, 4),
	//		UnicodeNSValidator(unicode.IsUpper, unicode.ToUpper),
	//	)}
	//	c := g.Coder()
	//	c.Validate("USER-404")   // nil
	//	c.Validate("USERS-404")  // ErrCode
	//	c.Validate("user-404")   // ErrCode
	CodeNSValidator NSValidator
	// CodeSeparator is the rune used to separate the NS and value within a Code constructed and/or parsed by a Coder.
	//
	// If zero or less, DefaultCodeSeparator will be used. Otherwise, it must be a printable rune otherwise a Coder will
	// always return an ErrCode when attempting to construct and/or parse a Code.
	//
	// For example;
	//
	//	g := &Generator{CodeSeparator: '.'}
	//	c := g.Coder("USER")
	//	c.MustBuild(404)  // "USER.404"
	CodeSeparator rune
	// CodeValueLen is the number of digits to be included in the value of a Code constructed and/or parsed by a Coder.
	//
	// If zero or less, a Code may contain any number of digits within its value so long as there's at least one.
	// Otherwise, a value cannot contain more digits than CodeValueLen and any value containing fewer digits will be
	// right-padded with zero.
	//
	// For example;
	//
	//	g := &Generator{CodeValueLen: 8}
	//	c := g.Coder("USER")
	//	c.MustBuild(404)  // "USER.40400000"
	CodeValueLen int
	// ContentType is the value used to populate the Content-Type header when Generator.WriteError or
	// Generator.WriteProblem are called without a WriteOptions.ContentType being passed. This also applies to the
	// Middleware functions as they call Generator.WriteError internally.
	//
	// If empty, ContentTypeJSONUTF8 will be used.
	ContentType string
	// LogArgKey is the key passed along with a Problem within the last two arguments to Generator.Logger.
	//
	// If empty, DefaultLogArgKey will be passed.
	//
	// This allows a somewhat more granular level of control without needing to provide a custom Logger.
	LogArgKey string
	// LogLeveler is the problem.LogLeveler used to override the LogLevel derived from a Type (i.e. instead of only
	// Type.LogLevel).
	//
	// If nil, Type.LogLevel will be used, with a fallback to DefaultLogLevel.
	//
	// For example;
	//
	//	leveler := func(defType Type) LogLevel {
	//		switch {
	//		case defType.Status == 0:
	//			return defType.LogLevel
	//		case def.Type.Status > 500:
	//			return LogLevelError
	//		default:
	//			return LogLevelWarn
	//		}
	//	}
	//	g := &Generator{LogLeveler: leveler}
	LogLeveler LogLeveler
	// Logger is the problem.Logger used by Generator.Log and Generator.LogContext to log a message along with any
	// arguments (incl. the Problem).
	//
	// If nil, DefaultLogger will be used.
	//
	// For example;
	//
	//	logger := slog.NewLogLogger(slog.NewJSONHandler(os.Stderr, nil), slog.LevelDebug)
	//	g := &Generator{Logger: LoggerFrom(logger)}
	Logger Logger
	// StackFlag provides control over the capturing of a stack trace and its visibility on a Problem.
	//
	// StackFlag is the default Flag. If Builder.Stack or WithStack are used, but no flags are provided, this is
	// considered equal to passing FlagField and FlagLog. This would mean that the UUID will be generated and the fully
	// visible on the Problem both in terms of field and within the logs. If FlagDisable is ever passed, all other flags
	// are ignored and the stack trace is not captured (or inherited) and will not be visible on the Problem.
	//
	// For example;
	//
	//	g := &Generator{StackFlag: FlagDisable}          // Stack trace is not captured or inherited
	//	g := &Generator{StackFlag: FlagField}            // Stack trace accessible via Problem.Stack
	//	g := &Generator{StackFlag: FlagLog}              // Stack trace visible only in logs
	//	g := &Generator{StackFlag: FlagField | FlagLog}  // Stack trace accessible via Problem.Stack and visible in logs
	StackFlag Flag
	// Translator is the problem.Translator used to provide localized values for translation keys, where possible, when
	// constructing a Problem.
	//
	// If nil, NoopTranslator will be used, which will always return an empty string, forcing the Problem to be
	// constructed using a fallback value for the associated field.
	//
	// For example;
	//
	//	translator := func(ctx context.Context, key any) string {
	//		t, ok := ctx.Value(contextKeyUT).(ut.Translator)
	//		if !ok {
	//			var err error
	//			if t, err = newUT(ctx); err != nil {
	//				return ""
	//			}
	//		}
	//		if v, err := t.T(key); err != nil {
	//			return ""
	//		} else {
	//			return v
	//		}
	//	}
	//	g := &Generator{Translator: translator}
	Translator Translator
	// Typer is the problem.Typer used to override the type URI reference derived from a Type (i.e. instead of only
	// Type.URI).
	//
	// If nil, Type.URI will be used, with a fallback to DefaultTypeURI.
	//
	// For example;
	//
	//	typer := func(defType Type) string {
	//		if defType.Status == http.StatusNotFound {
	//			return "https://datatracker.ietf.org/doc/html/rfc9110#name-404-not-found"
	//		}
	//		return defType.URI
	//	}
	//	g := &Generator{Typer: typer}
	Typer Typer
	// Unwrapper is the problem.Unwrapper used by Builder.Wrap and Wrap handle an already wrapped Problem in an error's
	// tree.
	//
	// If nil, PropagatedFieldUnwrapper will be used.
	//
	// For example;
	//
	//	g := &Generator{Unwrapper: FullUnwrapper()}
	Unwrapper Unwrapper
	// UUIDFlag provides control over the generation of an Universally Unique Identifier and its visibility on a
	// Problem.
	//
	// UUIDFlag is the default Flag. If Builder.UUID or WithUUID are used, but no flags are provided, this is considered
	// equal to passing FlagField and FlagLog. This would mean that the UUID will be generated and the fully visible on
	// the Problem both in terms of field and within the logs. If FlagDisable is ever passed, all other flags are
	// ignored and the UUID is not generated (or inherited) and will not be visible on the Problem.
	//
	// For example;
	//
	//	g := &Generator{UUIDFlag: FlagDisable}          // UUID not generated or inherited
	//	g := &Generator{UUIDFlag: FlagField}            // UUID accessible via Problem.UUID
	//	g := &Generator{UUIDFlag: FlagLog}              // UUID visible only in logs
	//	g := &Generator{UUIDFlag: FlagField | FlagLog}  // UUID accessible via Problem.UUID and visible in logs
	UUIDFlag Flag
	// UUIDGenerator returns the problem.UUIDGenerator used to generate a Universally Unique Identifier when
	// constructing a Problem.
	//
	// If nil, V4UUIDGenerator will be used to generate a UUID. A UUID is only generated for a Problem if needed. That
	// is; if Builder.UUID or WithUUID are used and not passed FlagDisable or, if not used, Generator.UUIDFlag contains
	// FlagField and/or FlagLog.
	//
	// For example;
	//
	//	nanoidGenerator := func(ng nanoid.generator, err error) UUIDGenerator {
	//		if err != nil {
	//			panic(err)
	//		}
	//		return func(_ context.Context) string {
	//			return ng()
	//		}
	//	}
	//	g := &Generator{UUIDGenerator: nanoidGenerator(nanoid.Canonic())}
	UUIDGenerator UUIDGenerator
}

Generator is responsible for generating a Problem. Its zero value (DefaultGenerator) is usable.

func GetGenerator

func GetGenerator(ctx context.Context) *Generator

GetGenerator returns the Generator within the given context.Context, otherwise DefaultGenerator.

func (*Generator) Build

func (g *Generator) Build() Builder

Build returns a Builder for the Generator with context.Background which can be used to construct problems.

func (*Generator) BuildContext

func (g *Generator) BuildContext(ctx context.Context) Builder

BuildContext returns a Builder for the Generator with the given context which can be used to construct problems.

func (*Generator) Coder

func (g *Generator) Coder(ns ...NS) Coder

Coder returns a Coder for the Generator, optionally with an NS.

An NS is only required when using Coder to build a Code but, when present when parsing a Code, it also validates that the parsed Code was constructed using the same NS.

func (*Generator) Log

func (g *Generator) Log(msg string, prob *Problem, args ...any)

Log logs the given message and Problem along with any additional arguments and context.Background via Generator.Logger.

The Problem is passed to Generator.Logger within the last two arguments; its key (Generator.LogArgKey) and value. If Generator.LogArgKey is empty, DefaultLogArgKey is used.

If Generator.Logger is nil, DefaultLogger is used to log the message.

func (*Generator) LogContext

func (g *Generator) LogContext(ctx context.Context, msg string, prob *Problem, args ...any)

LogContext logs the given message and Problem along with the context provided and any additional arguments via Generator.Logger.

The Problem is passed to Generator.Logger within the last two arguments; its key (Generator.LogArgKey) and value. If Generator.LogArgKey is empty, DefaultLogArgKey is used.

If Generator.Logger is nil, DefaultLogger is used to log the message.

func (*Generator) New

func (g *Generator) New(opts ...Option) *Problem

New returns a constructed Problem using context.Background, optionally using the options provided as well.

func (*Generator) NewContext

func (g *Generator) NewContext(ctx context.Context, opts ...Option) *Problem

NewContext returns a constructed Problem using the given context, optionally using the options provided as well.

func (*Generator) WriteError

func (g *Generator) WriteError(err error, w http.ResponseWriter, req *http.Request, probFunc func(err error) *Problem, opts ...WriteOptions) error

WriteError writes an HTTP response for a Problem where the Problem is unwrapped from err, where possible, with the given function being used to provide a default Problem, relying solely on WriteOptions.ContentType to determine how the response is formed, with a graceful fallback to Generator.ContentType and ContentTypeJSONUTF8. WriteOptions can also be passed for more granular control.

An error is returned if the Problem fails to be written to w.

func (*Generator) WriteErrorJSON

func (g *Generator) WriteErrorJSON(err error, w http.ResponseWriter, req *http.Request, probFunc func(err error) *Problem, opts ...WriteOptions) error

WriteErrorJSON writes an HTTP response for a Problem in JSON format where the Problem is unwrapped from err, where possible, with the given function being used to provide a default Problem. WriteOptions can also be passed for more granular control.

An error is returned if the Problem fails to be written to w.

func (*Generator) WriteErrorXML

func (g *Generator) WriteErrorXML(err error, w http.ResponseWriter, req *http.Request, probFunc func(err error) *Problem, opts ...WriteOptions) error

WriteErrorXML writes an HTTP response for a Problem in XML format where the Problem is unwrapped from err, where possible, with the given function being used to provide a default Problem. WriteOptions can also be passed for more granular control.

An error is returned if the Problem fails to be written to w.

func (*Generator) WriteProblem

func (g *Generator) WriteProblem(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblem writes an HTTP response for the given Problem, optionally using WriteOptions for more granular control, relying solely on WriteOptions.ContentType to determine how the response is formed, with a graceful fallback to Generator.ContentType and ContentTypeJSONUTF8.

An error is returned if prob fails to be written to w.

func (*Generator) WriteProblemJSON

func (g *Generator) WriteProblemJSON(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblemJSON writes an HTTP response for the given Problem in JSON format, optionally using WriteOptions for more granular control.

An error is returned if prob fails to be written to w.

func (*Generator) WriteProblemXML

func (g *Generator) WriteProblemXML(prob *Problem, w http.ResponseWriter, req *http.Request, opts ...WriteOptions) error

WriteProblemXML writes an HTTP response for the given Problem in XML format, optionally using WriteOptions for more granular control.

An error is returned if prob fails to be written to w.

type LogInfo

type LogInfo struct {
	// Level is the LogLevel that has either been explicitly defined during construction or inherited from a Type or
	// another Problem within an err's tree if unwrapped accordingly.
	Level LogLevel
	// Stack is a string representation of the stack trace captured during construction or inherited from another
	// Problem within an err's tree if unwrapped accordingly.
	//
	// Stack is only populated if Generator.StackFlag has FlagLog or either Builder.Stack or WithStack were used and
	// either passed no flags or FlagLog explicitly.
	Stack string
	// UUID is the Universally Unique Identifier generated during construction or inherited from another Problem
	// within an err's tree if unwrapped accordingly.
	//
	// UUID is only populated if Generator.UUIDFlag has FlagLog or either Builder.UUID or WithUUID were used and
	// either passed no flags or FlagLog explicitly.
	UUID string
}

LogInfo contains information associated with a Problem that is only relevant for logging purposes.

type LogLevel

type LogLevel uint

LogLevel represents a log level and will typically need mapped to one understood by any custom Logger.

It has built-in support for slog.Level when DefaultLogger or LoggerFrom are used.

The zero value is intentionally not mapped in order to represent an undefined value and should be substituted by a fallback/default LogLevel.

const (
	// LogLevelDebug represents the DEBUG log level.
	LogLevelDebug LogLevel = iota + 1
	// LogLevelInfo represents the INFO log level.
	LogLevelInfo
	// LogLevelWarn represents the WARN log level.
	LogLevelWarn
	// LogLevelError represents the ERROR log level.
	LogLevelError
)

type LogLeveler

type LogLeveler func(defType Type) LogLevel

LogLeveler is a function that can be used by a Generator to override the LogLevel derived from a Type (i.e. instead of only Type.LogLevel).

It is important to note that if the function returns zero, it will fall back to DefaultLogLevel and not Type.LogLevel.

type Logger

type Logger func(ctx context.Context, level LogLevel, msg string, args ...any)

Logger is a function used by a Generator to log a message and problem and any additional arguments.

The Problem is passed within the last two arguments; its key (Generator.LogArgKey) and value. If Generator.LogArgKey is empty, DefaultLogArgKey is passed.

func DefaultLogger

func DefaultLogger() Logger

DefaultLogger returns a Logger that uses slog.Default and is used by DefaultGenerator.

func LoggerFrom

func LoggerFrom(logger *slog.Logger) Logger

LoggerFrom returns a Logger that uses the given slog.Logger.

func NoopLogger

func NoopLogger() Logger

NoopLogger returns a Logger that does nothing.

type Matcher

type Matcher func(p *Problem) bool

Matcher is a function used to conditionally match on a Problem, returning true only if the match is successful.

A Matcher is never passed a nil pointer to a Problem.

func HasCode

func HasCode(code Code, operator ...Operator) Matcher

HasCode is used to match a Problem based on its Code.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasCodeNS

func HasCodeNS(ns NS, operator ...Operator) Matcher

HasCodeNS is used to match a Problem based on the NS within its Code using DefaultGenerator.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasCodeNSUsing

func HasCodeNSUsing(gen *Generator, ns NS, operator ...Operator) Matcher

HasCodeNSUsing is used to match a Problem based on the NS within its Code using the given Generator.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasCodeValue

func HasCodeValue(value uint, operator ...Operator) Matcher

HasCodeValue is used to match a Problem based on the value within its Code using DefaultGenerator.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasCodeValueUsing

func HasCodeValueUsing(gen *Generator, value uint, operator ...Operator) Matcher

HasCodeValueUsing is used to match a Problem based on the value within its Code using the given Generator.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasDetail

func HasDetail(detail string, operator ...Operator) Matcher

HasDetail is used to match a Problem based on its detail.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasExtension

func HasExtension(key string) Matcher

HasExtension is used to match a Problem based on whether it contains an extension with the given key.

func HasExtensions

func HasExtensions(keys ...string) Matcher

HasExtensions is used to match a Problem based on whether it contains extensions with the given keys.

func HasInstance

func HasInstance(instance string, operator ...Operator) Matcher

HasInstance is used to match a Problem based on its instance.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasStack

func HasStack() Matcher

HasStack is used to match a Problem based on whether it has a captured stack trace.

func HasStatus

func HasStatus(status int, operator ...Operator) Matcher

HasStatus is used to match a Problem based on its status.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasTitle

func HasTitle(title string, operator ...Operator) Matcher

HasTitle is used to match a Problem based on its title.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasType

func HasType(typeURI string, operator ...Operator) Matcher

HasType is used to match a Problem based on its type URI.

By default, this match is based on whether the values are equal, however, this can be controlled by passing another Operator.

func HasUUID

func HasUUID() Matcher

HasUUID is used to match a Problem based on whether it has a generated UUID.

func Or

func Or(matchers ...Matcher) Matcher

Or is used to match a Problem on any of the given matchers.

type NS

type NS string

NS represents a namespace within the problem generator (e.g. web application). It can be used to distinguish problems generated by different logical locations within the code base. This allows generic and possibly conflicting Code values to be reused across multiple namespaces while not inhibiting debugging.

For example; a problem generated within an authentication service may use a NS of "AUTH" while an user service may use a NS of "USER".

type NSValidator

type NSValidator func(ns NS) error

NSValidator is a function used validate a given NS when a Coder is constructing or parsing a Code.

Such validation can be useful to ensure each NS used meets a standard.

The error returned should not be an ErrCode as a Coder will wrap it in an ErrCode.

func ComposeNSValidator

func ComposeNSValidator(validators ...NSValidator) NSValidator

ComposeNSValidator returns a NSValidator composed of each of the given validators.

For example;

ComposeNSValidator(LenNSValidator(4, 8), UnicodeNSValidator(unicode.IsUpper, unicode.ToUpper))

func LenNSValidator

func LenNSValidator(min int, max ...int) NSValidator

LenNSValidator returns a NSValidator that asserts that a NS contains at least the minimum and, optionally, at most the maximum number of characters. Otherwise, an error is returned.

Since a Coder validates that a NS is not empty by default, min must be at least one and, if max is provided, must be greater than or equal to min.

For example;

LenNSValidator(4)
LenNSValidator(4, 8)

func RegexpNSValidator

func RegexpNSValidator(expr string) NSValidator

RegexpNSValidator returns a NSValidator that asserts that a NS matches the given regular expression. Otherwise, an error is returned.

If expr fails to compile into a regexp.Regexp, an error is always returned.

For example;

RegexpNSValidator(`[a-zA-Z]+`)

func UnicodeNSValidator

func UnicodeNSValidator(predicate func(r rune) bool, mapper func(r rune) rune) NSValidator

UnicodeNSValidator returns a NSValidator that asserts that a NS contains only unicode runes that meet the given predicate. Otherwise, an error is returned containing the desired rune using mapper.

For example;

UnicodeNSValidator(unicode.IsLower, unicode.ToLower)
UnicodeNSValidator(unicode.IsUpper, unicode.ToUpper)

type Operator

type Operator uint8

Operator is used by a Matcher to compare two values of the same type.

const (
	// OperatorEquals is used to check if two values of the same type are equal.
	OperatorEquals Operator = iota
	// OperatorNotEquals is used to check if two values of the same type are not equal.
	OperatorNotEquals
	// OperatorGreaterThan is used to check if one value is greater than another value of the same type.
	OperatorGreaterThan
	// OperatorGreaterThanOrEqual is used to check if one value is greater than or equal to another value of the same
	// type.
	OperatorGreaterThanOrEqual
	// OperatorLessThan is used to check if one value is less than another value of the same type.
	OperatorLessThan
	// OperatorLessThanOrEqual is used to check if one value is less than or equal to another value of the same type.
	OperatorLessThanOrEqual
)

type Option

type Option func(b *Builder)

Option is used to customize the generation of a Problem and/or to override fields derived from a Definition and/or Type.

An Option provides an alternative syntax for constructing problems using an underlying Builder as each Option simply maps to Builder method.

func FromDefinition

func FromDefinition(def Definition) Option

FromDefinition customizes a Generator to return a Problem using the given Definition when building a Problem.

The fields of def are treated as defaults when a field is not explicitly defined using another option. FromDefinition can conflict with FromType as it effectively assigns to the same underlying field.

func FromType

func FromType(defType Type) Option

FromType customizes a Generator to return a Problem using the given Type when building a Problem.

The fields of defType are treated as defaults when a field is not explicitly defined using another option. FromType can conflict with FromDefinition as it effectively assigns to the same underlying field, however, only setting Definition.Type.

func WithCode

func WithCode(code Code) Option

WithCode customizes a Generator to return a Problem with the given Code. See Problem.Code for more information.

If code is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options.

func WithDetail

func WithDetail(detail string) Option

WithDetail customizes a Generator to return a Problem with the given detail. See Problem.Detail for more information.

If detail is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options. However, if a localized detail is resolved from a translation key using WithDetailKey, that will take precedence over detail.

func WithDetailKey

func WithDetailKey(key any) Option

WithDetailKey customizes a Generator to return a Problem with detail localized using the given translation key. See Problem.Detail for more information.

The localized detail will be looked up using Generator.Translator, where possible. If resolved, it will take precedence over anything provided using WithDetail, FromDefinition, or any of the Wrap options.

func WithDetailKeyOrElse

func WithDetailKeyOrElse(key any, detail string) Option

WithDetailKeyOrElse is a convenient shorthand for using both WithDetailKey and WithDetail.

func WithDetailf

func WithDetailf(format string, args ...any) Option

WithDetailf customizes a Generator to return a Problem with the formatted detail. See Problem.Detail for more information.

If the formatted detail is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options. However, if a localized detail is resolved from a translation key using WithDetailKey, that will take precedence over the formatted detail.

func WithExtension

func WithExtension(key string, value any) Option

WithExtension customizes a Generator to return a Problem containing the given extension key and value. See Problem.Extensions for more information.

When used, it will take precedence over any extensions provided using FromDefinition or any of the Wrap options.

Panics if key is either empty or reserved (i.e. conflicts with Problem-level fields).

WithExtensions may be preferred for providing multiple extensions and does not conflict with usage of WithExtension in that neither option will delete/modify extensions unless the key overlaps, in which case the value will be overwritten.

func WithExtensions

func WithExtensions(extensions Extensions) Option

WithExtensions customizes a Generator to return a Problem with a shallow clone of the given extensions. See Problem.Extensions for more information.

If extensions is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options.

Panics if extensions contains a key that is either empty or reserved (i.e. conflicts with Problem-level fields).

WithExtension may be preferred for providing a single extension and does not conflict with usage of WithExtensions in that neither option will delete/modify extensions unless the key overlaps, in which case the value will be overwritten.

func WithInstance

func WithInstance(instanceURI string) Option

WithInstance customizes a Generator to return a Problem with the given instance URI reference. See Problem.Instance for more information.

An uri.Builder can be used to aid building the URI reference.

If instanceURI is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options.

func WithInstancef

func WithInstancef(format string, args ...any) Option

WithInstancef customizes a Generator to return a Problem with the formatted instance URI reference. See Problem.Instance for more information.

If the formatted instance URI reference is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options.

func WithLogLevel

func WithLogLevel(level LogLevel) Option

WithLogLevel customizes a Generator to return a Problem with the given LogLevel. See Problem.LogLevel for more information.

If level is not zero, it will take precedence over anything provided using FromDefinition, FromType, or any of the Wrap options.

func WithStack

func WithStack(flags ...Flag) Option

WithStack customizes a Generator to control if/how a captured stack trace is visible on a Problem. See Problem.Stack for more information.

By default, Generator.StackFlag is used to control visibility of a stack trace.

If no flags are provided, this is considered equal to passing FlagField and FlagLog. If FlagDisable is given, all other flags are ignored. No stack trace is captured if FlagDisable is provided.

If a stack trace needs to be captured and any of the Wrap options are used and a Problem is unwrapped that already has a stack trace, its stack trace will be used instead of recapturing a stack trace that could potentially result in loss of depth in terms of stack frames.

func WithStackFramesSkipped

func WithStackFramesSkipped(skipped int) Option

WithStackFramesSkipped customizes a Generator to skip the given number of additional frames if/when a stack trace is captured. See WithStack and Problem.Stack for more information.

If skipped is less than or equal to zero, no additional frames will be skipped.

func WithStatus

func WithStatus(status int) Option

WithStatus customizes a Generator to return a Problem with the given status. See Problem.Status for more information.

If status is not zero, it will take precedence over anything provided using FromDefinition, FromType, or any of the Wrap options.

func WithTitle

func WithTitle(title string) Option

WithTitle customizes a Generator to return a Problem with the given title. See Problem.Title for more information.

If title is not empty, it will take precedence over anything provided using FromDefinition, FromType, or any of the Wrap options. However, if a localized title is resolved from a translation key using WithTitleKey, that will take precedence over title.

func WithTitleKey

func WithTitleKey(key any) Option

WithTitleKey customizes a Generator to return a Problem with title localized using the given translation key. See Problem.Title for more information.

The localized title will be looked up using Generator.Translator, where possible. If resolved, it will take precedence over anything provided using WithTitle, FromDefinition, FromType, or any of the Wrap options.

func WithTitleKeyOrElse

func WithTitleKeyOrElse(key any, title string) Option

WithTitleKeyOrElse is a convenient shorthand for using both WithTitleKey and WithTitle.

func WithTitlef

func WithTitlef(format string, args ...any) Option

WithTitlef customizes a Generator to return a Problem with the formatted title. See Problem.Title for more information.

If the formatted title is not empty, it will take precedence over anything provided using FromDefinition or any of the Wrap options. However, if a localized title is resolved from a translation key using WithTitleKey, that will take precedence over the formatted title.

func WithType

func WithType(typeURI string) Option

WithType customizes a Generator to return a Problem with the given type URI reference. See Problem.Type for more information.

An uri.Builder can be used to aid building the URI reference.

If typeURI is not empty, it will take precedence over anything provided using FromDefinition, FromType, or any of the Wrap options.

func WithTypef

func WithTypef(format string, args ...any) Option

WithTypef customizes a Generator to return a Problem with the formatted type URI reference. See Problem.Type for more information.

If the formatted type URI reference is not empty, it will take precedence over anything provided using FromDefinition, FromType, or any of the Wrap options.

func WithUUID

func WithUUID(flags ...Flag) Option

WithUUID customizes a Generator to control if/how a generated UUID is visible on a Problem. See Problem.UUID for more information.

By default, Generator.UUIDFlag is used to control visibility of a UUID.

If no flags are provided, this is considered equal to passing FlagField and FlagLog. If FlagDisable is given, all other flags are ignored. No UUID is generated if FlagDisable is provided.

If a UUID needs to be generated and any of the Wrap options are used and a Problem is unwrapped that already has a UUID, its UUID will be used instead of generating a UUID to ensure that it is used consistently once generated to make tracing any logs, specifically, a lot easier.

func Wrap

func Wrap(err error, unwrapper ...Unwrapper) Option

Wrap customizes a Generator to return a Problem wrapping the given error. See Problem.Error and Problem.Unwrap for more information.

Additionally, more control can be achieved over the scenario where err's tree contains a Problem by passing an Unwrapper; a function responsible for deciding what, if any, information from a wrapped Problem is to be used when building a Problem. Any such information will not take precedence over any explicitly defined Problem fields, however, it will take precedence over any information derived from a Definition or its Type using FromDefinition and/or FromType.

If no Unwrapper is provided, Generator.Unwrapper is used from Builder.Generator if not nil, otherwise from DefaultGenerator. If an Unwrapper could still not be resolved, it defaults to PropagatedFieldUnwrapper.

type ParsedCode

type ParsedCode struct {
	// Code is the Code which was parsed.
	Code Code
	// NS is the namespace found within the parsed Code.
	NS NS
	// Value is the value found within the parsed Code.
	Value uint
}

ParsedCode contains any information parsed from a Code, including the original Code.

func MustParseCode

func MustParseCode(code Code, ns ...NS) ParsedCode

MustParseCode is a convenient shorthand for calling Coder.MustParse on a Coder using DefaultGenerator and optionally a given NS.

func ParseCode

func ParseCode(code Code, ns ...NS) (ParsedCode, error)

ParseCode is a convenient shorthand for calling Coder.Parse on a Coder using DefaultGenerator and optionally a given NS.

type Problem

type Problem struct {
	// Code is a unique Code that identifies the specific occurrence of the Problem.
	//
	// When present, consumers can use Code to communicate with the owner of the generator to help debug the problem
	// and the client can use it to handle a specific occurrence of the problem without risk of conflicts with other
	// problems using the same Status, Title, and/or Type.
	//
	// It MUST NOT change from occurrence to occurrence of the problem.
	Code Code `json:"code,omitempty" xml:"code,omitempty"`
	// Detail is a human-readable explanation specific to this occurrence of the Problem.
	//
	// When present, ought to focus on helping the client correct the problem, rather than giving debugging
	// information.
	//
	// Consumers SHOULD NOT parse Detail for information; instead Extensions is a more suitable and less error-prone
	// way to obtain such information.
	Detail string `json:"detail,omitempty" xml:"detail,omitempty"`
	// Extensions may contain additional information used extend the details of the Problem.
	//
	// Clients consuming problem details MUST ignore any such extensions that they don't recognize; this allows
	// problem types to evolve and include additional information in the future.
	//
	// If/when the Problem is marshalled to JSON or XML any such extensions are serialized at the top level.
	// However, such information is lost if an attempt is made to unmarshal the serialized data bac into XML due to
	// limitations with xml.Unmarshaler. JSON data can be unmarshaled without any issues. If Extensions contains a
	// key that is empty or reserved (i.e. conflicts with Problem-level fields), an error will occur when attempting
	// to marshal the Problem to JSON or XML.
	Extensions Extensions `json:"-" xml:"extensions,omitempty"`
	// Instance is a URI reference that identifies the specific occurrence of the Problem.
	//
	// It may or may not yield further information if dereferenced.
	//
	// It may be a relative URI; this means that it must be resolved relative to the document's base URI.
	Instance string `json:"instance,omitempty" xml:"instance,omitempty"`
	// Stack is a string representation of the stack trace captured when the Problem generated.
	//
	// When present, Stack can be used to help debug the problem, however, care should be taken as a stack trace
	// will contain information about the internal system architecture and therefore potentially pose a risk to
	// information leakage.
	//
	// Stack is only populated if Generator.StackFlag has FlagField or either Builder.Stack or WithStack were used
	// and either passed no flags or FlagField explicitly. If FlagField is not present but FlagLog is, the Problem
	// will contain a capture stack trace internally for logging within LogValue, however, Stack will be empty. This
	// can be useful for cases where a stack trace is desired for logging only.
	Stack string `json:"stack,omitempty" xml:"stack,omitempty"`
	// Status is the HTTP status code to be generated by the origin server for this occurrence of the Problem.
	//
	// When present, is only advisory; it conveys the HTTP status code used for the convenience of the consumer.
	// Generators MUST use the same status code in the actual HTTP response, to assure that generic HTTP software
	// that does not understand this format still behaves correctly.
	//
	// Consumers can use Status to determine what the original status code used by the generator was, in cases where
	// it has been changed (e.g. by an intermediary or cache), and when message bodies persist without HTTP
	// information. Generic HTTP software will still use the HTTP status code.
	Status int `json:"status" xml:"status"`
	// Title is a short, human-readable summary of the type of the Problem.
	//
	// It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization (e.g.
	// using proactive content negotiation).
	Title string `json:"title" xml:"title"`
	// Type is a URI reference that identifies the type of the Problem.
	//
	// It is encouraged that, when dereferenced, it provides human-readable documentation for the problem type (e.g.
	// using HTML). DefaultTypeURI should be used in all cases where Type is absent.
	//
	// Consumers MUST use Type as the primary identifier for the problem type; Title is advisory and included only
	// for users who are not aware of the semantics of the URI and do not have the ability to discover them (e.g.
	// offline log analysis). Consumers SHOULD NOT automatically dereference the type URI.
	//
	// It may be a relative URI; this means that it must be resolved relative to the document's base URI.
	Type string `json:"type" xml:"type"`
	// UUID is the Universally Unique Identifier generated along with the Problem using Generator.UUIDGenerator.
	//
	// When present, it MUST be unique so that consumers can use UUID to communicate with the owner of the generator
	// to help debug the problem.
	//
	// UUID is only populated if Generator.UUIDFlag has FlagField or either Builder.UUID or WithUUID were used and
	// either passed no flags or FlagField explicitly. If FlagField is not present but FlagLog is, the Problem will
	// contain a generated "UUID" internally for logging within LogValue, however, UUID will be empty. This can be
	// useful for cases where a "UUID" is desired for logging only.
	UUID string `json:"uuid,omitempty" xml:"uuid,omitempty"`
	// contains filtered or unexported fields
}

Problem describes problem details for HTTP APIs based on RFC 9457; https://datatracker.ietf.org/doc/html/rfc9457.

While a Problem can be explicitly constructed, it's expected that either a Builder or New (with options) is used to construct a Problem for the greatest level of control and for fallback/default fields to be applied as well as support for wrapping errors. Construction is typically driven by a Generator which, unless defined, will be the DefaultGenerator.

func As

func As(err error) (*Problem, bool)

As is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

func AsMatch

func AsMatch(err error, matchers ...Matcher) (*Problem, bool)

AsMatch is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

Additionally, if a Problem is found in err's tree, it must match all matchers provided, otherwise it will be unwrapped, and it's tree (excluding itself) will continue to be checked until either a matching Problem is found or no Problem is found.

func AsMatchOrElse

func AsMatchOrElse(err error, defaultProb *Problem, matchers ...Matcher) *Problem

AsMatchOrElse is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

Additionally, if a Problem is found in err's tree, it must match all matchers provided, otherwise it will be unwrapped, and it's tree (excluding itself) will continue to be checked until either a matching Problem is found or no Problem is found.

If no matching Problem is found in err's tree, defaultProb is returned but false is also returned to indicate that the default was used. This can be useful for providing a default Problem if one is not found in err's tree. For example; error middleware may use AsMatchOrElse with a catch-all Problem for cases where an error's tree has no Problem to include in the response.

func AsMatchOrElseGet

func AsMatchOrElseGet(err error, defaultProbFunc func() *Problem, matchers ...Matcher) *Problem

AsMatchOrElseGet is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

Additionally, if a Problem is found in err's tree, it must match all matchers provided, otherwise it will be unwrapped, and it's tree (excluding itself) will continue to be checked until either a matching Problem is found or no Problem is found.

If no match Problem is found in err's tree, defaultProbFunc is used to return a Problem but false is also returned to indicate that the default was used. This can be useful for lazily building/constructing a default Problem if one is not found in err's tree. For example; error middleware may use AsMatchOrElseGet to lazily construct a catch-all Problem for cases where an error's tree has no Problem to include in the response.

func AsOrElse

func AsOrElse(err error, defaultProb *Problem) (*Problem, bool)

AsOrElse is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

If no Problem is found in err's tree, defaultProb is returned but false is also returned to indicate that the default was used. This can be useful for providing a default Problem if one is not found in err's tree. For example; error middleware may use AsOrElse with a catch-all Problem for cases where an error's tree has no Problem to include in the response.

func AsOrElseGet

func AsOrElseGet(err error, defaultProbFunc func() *Problem) (*Problem, bool)

AsOrElseGet is a convenient shorthand for calling errors.As with a Problem target, however, it also gracefully handles the case where err is nil without a panic.

If no Problem is found in err's tree, defaultProbFunc is used to return a Problem but false is also returned to indicate that the default was used. This can be useful for lazily building/constructing a default Problem if one is not found in err's tree. For example; error middleware may use AsOrElseGet to lazily construct a catch-all Problem for cases where an error's tree has no Problem to include in the response.

func New

func New(opts ...Option) *Problem

New is a convenient shorthand for calling Generator.New on DefaultGenerator.

func NewContext

func NewContext(ctx context.Context, opts ...Option) *Problem

NewContext is a convenient shorthand for calling Generator.NewContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator.

func (*Problem) Error

func (p *Problem) Error() string

Error returns the most suitable error message for the Problem.

If the Problem wraps another error, the message of that error will be included.

func (*Problem) Extension

func (p *Problem) Extension(key string) (value any, found bool)

Extension returns the value of the extension with given key within the Problem, if present.

func (*Problem) LogInfo

func (p *Problem) LogInfo() LogInfo

LogInfo returns information associated with the Problem that is only relevant for logging purposes.

It is mostly intended to be used when using a custom Logger as a means of providing an alternative to Problem.LogValue, which is limited to supporting slog.Logger.

func (*Problem) LogValue

func (p *Problem) LogValue() slog.Value

LogValue returns a slog.GroupValue representation of the Problem containing attrs for only non-empty fields.

func (*Problem) MarshalJSON

func (p *Problem) MarshalJSON() ([]byte, error)

MarshalJSON marshals the Problem into JSON.

This is required in order to allow Problem.Extensions to be marshaled at the top-level of a Problem. Unfortunately, this can only be managed by marshaling the problem details twice so is suboptimal in terms of performance.

An error is returned if unable to marshal the Problem or Problem.Extensions contains a key that is either empty or reserved (i.e. conflicts with Problem-level fields).

func (*Problem) MarshalXML

func (p *Problem) MarshalXML(e *xml.Encoder, start xml.StartElement) error

MarshalXML marshals the Problem into XML.

This is required in order for greater control of the local and space names on the xml.StartElement when their default values are expected. In such cases, it's preferred to use local and space names that match RFC 9457.

An error is returned if unable to marshal the Problem or Problem.Extensions contains a key that is either empty or reserved (i.e. conflicts with Problem-level fields).

func (*Problem) String

func (p *Problem) String() string

String returns a string representation of the Problem.

func (*Problem) UnmarshalJSON

func (p *Problem) UnmarshalJSON(data []byte) error

UnmarshalJSON unmarshals the JSON data provided into the Problem.

This is required in order to unmarshal any superfluous JSON properties at the top-level into Problem.Extensions. Unfortunately, this can only be managed by unmarshaling the JSON twice so is suboptimal in terms of performance.

An error is returned if unable to unmarshal data.

func (*Problem) Unwrap

func (p *Problem) Unwrap() error

Unwrap returns the error wrapped by the Problem, if any, otherwise returns nil.

type Translator

type Translator func(ctx context.Context, key any) string

Translator is a function that returns a localized value based on the translation key provided.

An empty string must always be returned if no localized value could be found for key, even if there was an internal error. This allows a Problem to be constructed using a fallback value for the associated field.

func NoopTranslator

func NoopTranslator() Translator

NoopTranslator returns a Translator that always returns an empty string, forcing the Problem to be constructed using a fallback value for the associated field.

type Type

type Type struct {
	// LogLevel is the default LogLevel to be assigned to a Problem generated from the Type. See Problem.LogLevel for
	// more information.
	//
	// The LogLevel may be overridden by Generator.LogLeveler.
	//
	// If LogLevel is zero, the default used is DefaultLogLevel.
	LogLevel LogLevel `json:"logLevel" xml:"logLevel" yaml:"logLevel"`
	// Status is the default status to be assigned to a Problem generated from the Type. See Problem.Status for more
	// information.
	//
	// If Status is zero, the default used is http.StatusInternalServerError.
	Status int `json:"status" xml:"status" yaml:"status"`
	// Title is the default title to be assigned to a Problem generated from the Type. See Problem.Title for more
	// information.
	//
	// If Title is empty, the default used is DefaultTitle.
	//
	// If TitleKey is not empty and can be resolved it will take precedence over Title.
	Title string `json:"title" xml:"title" yaml:"title"`
	// TitleKey is the translation key of the default title to be assigned to a Problem generated from the Type. See
	// Problem.Title for more information.
	//
	// The localized title will be looked up using Generator.Translator, where possible. If resolved, it will take
	// precedence over Title.
	//
	// If TitleKey is empty it, it is ignored.
	TitleKey any `json:"titleKey" xml:"titleKey" yaml:"titleKey"`
	// URI is the default type URI to be assigned to a Problem generated from the Type. See Problem.Type for more
	// information.
	//
	// An uri.Builder can be used to aid building the URI reference.
	//
	// If URI is empty, the default used is DefaultTypeURI.
	URI string `json:"uri" xml:"uri" yaml:"uri"`
}

Type represents a reusable problem type that may contain default values that can be used when generating a Problem from a specific type.

A Type, optionally used in combination with a Definition, provides an alternative approach to generating problems where they can be used to dictate all information populated within a Problem and/or combined with options to provide more granular control and overrides.

func (Type) Build

func (t Type) Build() Builder

Build is a convenient shorthand for calling Generator.Build on DefaultGenerator with the Type already passed to Builder.DefinitionType.

func (Type) BuildContext

func (t Type) BuildContext(ctx context.Context) Builder

BuildContext is a convenient shorthand for calling Generator.BuildContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator, with the Type already passed to Builder.DefinitionType.

func (Type) BuildContextUsing

func (t Type) BuildContextUsing(ctx context.Context, gen *Generator) Builder

BuildContextUsing is a convenient shorthand for calling Generator.BuildContext with the Type already passed to Builder.DefinitionType.

func (Type) BuildUsing

func (t Type) BuildUsing(gen *Generator) Builder

BuildUsing is a convenient shorthand for calling Generator.Build with the Type already passed to Builder.DefinitionType.

func (Type) New

func (t Type) New(opts ...Option) *Problem

New is a convenient shorthand for calling Generator.New on DefaultGenerator, including FromType with the Type along with any specified options.

func (Type) NewContext

func (t Type) NewContext(ctx context.Context, opts ...Option) *Problem

NewContext is a convenient shorthand for calling Generator.NewContext on the Generator within the given context.Context, if any, otherwise DefaultGenerator, including FromType with the Type along with any specified options.

func (Type) NewContextUsing

func (t Type) NewContextUsing(ctx context.Context, gen *Generator, opts ...Option) *Problem

NewContextUsing is an alternative for calling Generator.NewContext, including FromType with the Type along with any specified options.

func (Type) NewUsing

func (t Type) NewUsing(gen *Generator, opts ...Option) *Problem

NewUsing is an alternative for calling Generator.New, including FromType with the Type along with any specified options.

type Typer

type Typer func(defType Type) string

Typer is a function that can be used by a Generator to override the type URI reference derived from a Type (i.e. instead of only Type.URI).

An uri.Builder can be used to aid building the URI reference.

It is important to note that if the function returns an empty string, it will fall back to DefaultTypeURI and not Type.URI.

type UUIDGenerator

type UUIDGenerator func(ctx context.Context) string

UUIDGenerator is a function used by a Generator to generate a Universally Unique Identifier.

func V4UUIDGenerator

func V4UUIDGenerator() UUIDGenerator

V4UUIDGenerator returns a UUIDGenerator that generates a (V4) UUID and is used by DefaultGenerator.

The strength of the generated UUIDs are based on the strength of the crypto/rand package.

Uses the randomness pool if it was enabled with uuid.EnableRandPool.

func V4UUIDGeneratorFromReader

func V4UUIDGeneratorFromReader(reader io.Reader) UUIDGenerator

V4UUIDGeneratorFromReader returns a UUIDGenerator that generates a (V4) UUID based on bytes read from the given reader.

type Unwrapper

type Unwrapper func(err error) Problem

Unwrapper is a function used by Builder.Wrap and Wrap to handle an already wrapped Problem in err's tree.

An Unwrapper is effectively responsible for deciding what, if any, information from a wrapped Problem is to be used to construct the new Problem. Any such information will not take precedence over any explicitly defined Problem fields, however, it will take precedence over any information derived from a Definition or its Type.

func FullUnwrapper

func FullUnwrapper() Unwrapper

FullUnwrapper returns an Unwrapper that extracts all fields from a wrapped Problem in err's tree, if present. These fields will not take precedence over any explicitly defined Problem fields, however, it will take precedence over any fields derived from a Definition or its Type.

func NoopUnwrapper

func NoopUnwrapper() Unwrapper

NoopUnwrapper returns an Unwrapper that does nothing.

func PropagatedFieldUnwrapper

func PropagatedFieldUnwrapper() Unwrapper

PropagatedFieldUnwrapper returns an Unwrapper that extracts only fields that are expected to be propagated (e.g. captured stack trace, generated "UUID") from a wrapped Problem in err's tree, if present. Any such fields will not take precedence over any explicitly defined Problem fields, however, it will take precedence over any fields derived from a Definition or its Type.

type WriteOptions

type WriteOptions struct {
	// ContentType is the content/media type to be used in the HTTP response.
	//
	// The value will be ignored if unsupported or not appropriate for the function called. If empty,
	// Generator.ContentType will be used with a fallback to either ContentTypeJSONUTF8 or a more appropriate
	// content/media type depending on the function called.
	ContentType string
	// LogArgs contains arguments to be passed to Generator.LogContext along with the Problem.
	//
	// If empty, no additional arguments will be passed.
	LogArgs []any
	// LogDisabled is whether the Problem should be logged via Generator.LogContext.
	//
	// By default, the Problem will be logged unless the resolved LogLevel for the Problem is disabled for
	// Generator.Logger.
	LogDisabled bool
	// LogMessage is the message to be passed to Generator.LogContext along with the Problem.
	//
	// If empty, a basic message will be passed.
	LogMessage string
	// Status is the status code to the written to the HTTP response.
	//
	// If less than or equal to zero, Problem.Status will be used with a fallback to http.StatusInternalServerError.
	Status int
}

WriteOptions contains options that can be used when writing errors/problems to HTTP responses.

All fields are optional with default behaviour clearly documented.

Directories

Path Synopsis
Package http provides built-in reusable a problem.Definition and problem.Type for each known HTTP error.
Package http provides built-in reusable a problem.Definition and problem.Type for each known HTTP error.
internal
buffer
Package buffer provides a thin wrapper around a byte slice as well as portion of the strconv package's zero-allocation formatters.
Package buffer provides a thin wrapper around a byte slice as well as portion of the strconv package's zero-allocation formatters.
stack
Package stack provides support for capturing stack traces efficiently.
Package stack provides support for capturing stack traces efficiently.
Package uri provides support for constructing URI references to be used within problems.
Package uri provides support for constructing URI references to be used within problems.

Jump to

Keyboard shortcuts

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