Documentation
¶
Overview ¶
The serum package provides helper functions and handy types for error handling that works in accordance with the Serum Errors Convention.
You don't need to use this package to implement the Serum Errors Convention! (A key goal of the Convention is that you *do not* need any specific implementation; the convention is based on the serial forms, and in Golang, even all of the static analysis tooling is based on interfaces.) However, you may find it handy.
Index ¶
- func Cause(err error) error
- func Code(err error) string
- func Detail(err error, whichDetail string) string
- func Details(err error) [][2]string
- func DetailsMap(err error) map[string]string
- func Error(ecode string, params ...WithConstruction) error
- func Errorf(ecode string, fmtPattern string, args ...interface{}) error
- func Message(err error) string
- func SynthesizeString(err ErrorInterface) string
- func ToJSON(err error) ([]byte, error)
- func ToJSONString(err error) string
- type Data
- type ErrorInterface
- type ErrorInterfaceWithCause
- type ErrorInterfaceWithDetailsMap
- type ErrorInterfaceWithDetailsOrdered
- type ErrorInterfaceWithMessage
- type ErrorValue
- type WithConstruction
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Cause ¶
Cause returns the cause of any Serum-style error.
This function takes the general "error" type and feature-detects for Serum behaviors, but still has fallback behaviors for any error value.
If the given error is not recognizably Serum-styled, this function falls back to golang's standard `errors.Unwrap`.
(This function is really only provided for completeness and consistency of naming; it's functionally identical to golang's standard `errors.Unwrap`.)
func Code ¶
Code will access and return the error code for any Serum-style error.
This function takes the general "error" type and feature-detects for Serum behaviors, but still has fallback behaviors for any error value.
If the given error is _not_ recognizably Serum-styled, a code string will be invented on the fly. This invented code string will have the prefix "bestguess-golang-" followed by a munge of the golang type name. This fallback is meant to be minimally functional and help find the source of coding mistakes that lead to its creation, but should not be seen in a well-formed program.
func Detail ¶
Detail gets a detail value out of an error, or returns empty string if there isn't one. It's functionally equal to `DetailsMap()[whichDetail]`, but may be more efficient.
func Details ¶
Details returns the details key-values of an error as a slice of pairs of strings.
This function takes the general "error" type and feature-detects for Serum behaviors, but still has fallback behaviors for any error value.
If the given error is not recognizably Serum-styled, this function returns nil.
Note that you may also be able to use the DetailsMap to get the same content as a golang map, for convenience, but be aware that access mechanism does not support order-preservation, and may often be slightly slower performance.
The result should not be mutated; it may be the original memory from the error value.
func DetailsMap ¶
DetailsMap returns the details of an error as a map.
This function takes the general "error" type and feature-detects for Serum behaviors, but still has fallback behaviors for any error value.
Note that you may wish to use the Details function instead, if the original ordering of the details fields is important; because it uses golang maps, this function is not order-preserving.
If the given error is not recognizably Serum-styled, this function returns an empty map.
The map should not be mutated; it may be the original memory from the error value.
func Error ¶
func Error(ecode string, params ...WithConstruction) error
Error is a constructor for new Serum-style error values, supporting use of templated messages, and attachment of details, causes, and the enter suite of Serum features.
The error code parameter is required, and all other parameters are optional. The `serum.With*()` functions are used to create descriptions of messages and details attachements, and these are then provided as varargs as desired.
See the examples for usage.
Errors:
- param: ecode -- the error code to construct.
Example ¶
const ErrJobNotFound = "demo-error-job-not-found"
constructor := func(param int) error {
return serum.Error(ErrJobNotFound,
serum.WithMessageTemplate("job ID {{ID}} not found"),
serum.WithDetail("ID", strconv.Itoa(param)),
)
}
err := constructor(12)
fmt.Printf("the error as a string:\n\t%v\n", err)
jb, jsonErr := json.MarshalIndent(err, "\t", "\t")
if jsonErr != nil {
panic(jsonErr)
}
fmt.Printf("the error as json:\n\t%s\n", jb)
Output: the error as a string: demo-error-job-not-found: job ID 12 not found the error as json: { "code": "demo-error-job-not-found", "message": "job ID 12 not found", "details": { "ID": "12" } }
func Errorf ¶
Errorf produces new Serum-style error values, and attaches a message, which may use a formatting pattern.
Provide the error code string as the first parameter, and a message as the second parameter. The message may be a format string as per `fmt.Errorf` and friends, and additional parameters can be given as varargs.
If a %w verb is used, Errorf will take an error parameter in the args and attach it as "cause", similarly to the behavior of `fmt.Errorf`. However, if that error is not already a Serum-style error (concretely: if it does not implement ErrorInterface), it will be coerced into one, by use of the Standardize function. (We consider this coersion appropriate to perform immediately, because otherwise the resulting value would fail to round-trip through serialization.)
Errors:
- param: ecode -- the error code to construct.
Example ¶
const ErrFoobar = "demo-error-foobar"
err := serum.Errorf(ErrFoobar, "freetext goes here (%s)", "and can interpolate")
fmt.Printf("the error as a string:\n\t%v\n", err)
jb, jsonErr := json.MarshalIndent(err, "\t", "\t")
if jsonErr != nil {
panic(jsonErr)
}
fmt.Printf("the error as json:\n\t%s\n", jb)
Output: the error as a string: demo-error-foobar: freetext goes here (and can interpolate) the error as json: { "code": "demo-error-foobar", "message": "freetext goes here (and can interpolate)" }
func Message ¶
Message returns the message field of a Serum-style error.
This function takes the general "error" type and feature-detects for Serum behaviors, but still has fallback behaviors for any error value. It returns the same as (error).Error() for other errors.
If you are producing text for a user, consider the SynthesizeString function. Since the message field is optional in Serum, it may be blank; it is also defined as _not_ including the error's code. The SynthesizeString function will produce a human-readable string containing the code as well as the message, if it's present.
func SynthesizeString ¶
func SynthesizeString(err ErrorInterface) string
SynthesizeString generates a string for an error, suitable for return as the golang `Error() string` result. SynthesizeString will detect properties of a Serum error, and synthesize a string using them. The string will contain the code, the message, and the string of the cause if present, in roughly the form "{code}[: {message}][: caused by: {cause}]". Entries from a details map will not be present (unless the message includes them), as per the Serum standard's recommendation.
You can use this function to implement the `Error() string` method of a Serum error type conveniently.
The resultant string is hoped to be human-readable. It is not expected to be mechanically parsible. The form is primarily meant to match Golang community norms; it is not a Serum convention.
The exact behavior of this function may change over time. For example, currently, it disregards all linebreaks (it neither strips nor introduces them itself), but in the future, if a Serum convention for multiline errors is introduced, then this function will likely change in behavior to match.
func ToJSON ¶
ToJSON is a helper function to turn any error into JSON. It is suitable to use in implementing `encoding/json.Marshaler.MarshalJSON` if implementing your own error types, and it is used for that purpose in the ErrorValue type provided by this package. If is also suitable for freestanding use on any error value (even non-Serum error values).
If the error is a Serum error (per ErrorInterface), we'll serialize it completely, including message, details, code, and cause, all distinctly. If the error is not a Serum error, we'll serialize it anyway, but fudge as necessary to produce a result that will at least be Serum serial spec compliant and able to be deserialized as a Serum error.
In fudge mode: the golang type will appear as part of the serum code; the `Error() string` will be used as a message; `errors.Unwrap` will be used to find a cause; etc.
func ToJSONString ¶ added in v0.6.0
ToJSONString is similar to ToJSON, but returns exactly one value, which makes it easy to use in chained calls, or to hand to a Printf parameter.
fmt.Sprintf(os.Stderr, "%s\n", serum.ToJSONString(err))
... is an easy way to wrap up your program!
Types ¶
type Data ¶
type Data struct {
Code string
Message string
Details [][2]string
Cause ErrorInterface
}
Data is the body of the ErrorValue type.
It is a separate type mainly for naming purposes (it allows us to have the same name for fields here as we use for the accessor methods on ErrorValue).
Most user code will not see this type. Although it is exported, and referencing it is allowed, it is not usually necessary. User code can construct these values if desired, but using constructor functions from the go-serum package is often syntactically easier. User code may access these values directly if it's known that the code is handling ErrorValue concretely, but most code is not writen in such a way, and the serum accessor functions are used instead.
type ErrorInterface ¶
ErrorInterface is the minimal interface that must be implemented to be a Serum error. This is also the interface that go-serum-analyzer is looking for, if you use that tool (although not by name; matching the pattern is sufficient).
This interface is not often seen in user code. Usually, we still recommend user code mostly refers to "error", as is normal in golang. Functions throughout this package will accept and return error as a parameter, and apply serum behaviors to those values by use of feature detection, which removes all need to refer to this interface in user code.
func Standardize ¶
func Standardize(other error) ErrorInterface
Standardize returns a value that's guaranteed to be a Serum-style error, and use the concrete type of *ErrorValue from this package.
This isn't often necessary to use, because all the functions in this package accept any error implementation and figure out how to do the right thing -- but is provided for your convenience if needed for creating new errors or just moving things into a standard memory layout for some reason.
If given a value that implements the Serum interfaces, all data will be copied, using those interfaces to access it.
If given a golang error that's not a Serum-style error at all, the same procedure is followed: a new value will be created, where the code is set to what `serum.Code` returns on the old value; etc. (In practice, this means you'll end up with an ErrorValue that contains a code string that is prefixed with "golang-bestguess-"; etc.)
If given a value that is already of type *ErrorValue, it is returned unchanged.
This function returns ErrorInterface rather than concretely *ErrorValue, to reduce the chance of creating "untyped nil" problems in practical usage, but it is valid to directly cast the result to *ErrorValue if you wish.
type ErrorInterfaceWithCause ¶
type ErrorInterfaceWithCause interface {
ErrorInterface
Unwrap() error
}
type ErrorInterfaceWithDetailsMap ¶
type ErrorInterfaceWithDetailsMap interface {
ErrorInterface
Details() map[string]string
}
type ErrorInterfaceWithDetailsOrdered ¶
type ErrorInterfaceWithDetailsOrdered interface {
ErrorInterface
Details() [][2]string
}
type ErrorInterfaceWithMessage ¶
type ErrorInterfaceWithMessage interface {
ErrorInterface
Message() string
}
type ErrorValue ¶
type ErrorValue struct {
Data
}
ErrorValue is a concrete type that implements the Serum conventions for errors.
It can contain message and details fields in addition to the essential "code" field, and implements convenient features like automatic synthesis of a good message for golang `Error() string`, as well as supporting json marshalling and unmarshalling.
Accessor methods can be used to inspect the values inside this type, but typically, the package-scope functions in serum should be used instead -- `serum.Code`, `serum.Message`, `serum.Details`, etc -- because they are easier to use without refering to any concrete types. (Using the package-scope functions will save you from any syntactical line-noise of casting!)
The fields of this type are exported, but mutating them is inadvisable. (The go-serum-analyzer tool becomes much less useful if you do so; it does not support tracking the effects of such mutations.)
func (*ErrorValue) Code ¶
func (e *ErrorValue) Code() string
Code returns the Serum errorcode. Use the `serum.Code` package function to access this without referring to the concrete type.
func (*ErrorValue) Details ¶
func (e *ErrorValue) Details() [][2]string
Details returns the Serum details key-values. Use the `serum.Details` or `serum.DetailsMap` package function to access this without referring to the concrete type.
func (*ErrorValue) Error ¶
func (e *ErrorValue) Error() string
Error implements the golang error interface. The returned string will contain the code, the message if present, and the string of the cause. Per Serum convention, it does not include any of the details fields.
func (*ErrorValue) MarshalJSON ¶
func (e *ErrorValue) MarshalJSON() ([]byte, error)
func (*ErrorValue) Message ¶
func (e *ErrorValue) Message() string
Message returns the Serum message. Use the `serum.Message` package function to access this without referring to the concrete type.
func (*ErrorValue) UnmarshalJSON ¶
func (e *ErrorValue) UnmarshalJSON(b []byte) error
func (*ErrorValue) Unwrap ¶
func (e *ErrorValue) Unwrap() error
Unwrap returns the Serum cause. Use the `serum.Cause` package function, or the golang `errors.Unwrap` function, to access this without referring to the concrete type.
type WithConstruction ¶
type WithConstruction struct {
// contains filtered or unexported fields
}
WithConstruction is a data carrier type used as part of the Error constructor system. It is not usually seen directly in user code; only passed between the With*() functions, and directly into the Error constructor function.
See the examples of the Error function for complete demonstrations of usage.
func WithCause ¶
func WithCause(cause error) WithConstruction
WithDetail is part of the system for constructing an error with the serum.Error function. It can accept any golang error value and will attach it as a cause to the newly produced Serum error.
As with Errorf's behavior when attaching causes, if the given error is not already Serum-style error, it will be coerced into one. This may result in a generated error code, which is prefixed with the string "bestguess-golang-" and some type name information.
func WithDetail ¶
func WithDetail(key, value string) WithConstruction
WithDetail is part of the system for constructing an error with the serum.Error function. It allows attaching simple key:value string pairs to the error.
In addition to being stored as details in Serum convention (e.g., these values will be serialized as entries in a map when serializing the error), the WithMessageTemplate system can reference the detail values.
See the examples of the Error function for complete demonstrations of usage.
func WithMessageLiteral ¶
func WithMessageLiteral(s string) WithConstruction
WithMessageLiteral is part of the system for constructing an error with the serum.Error function.
In contrast with WithMessageTemplate, this string is always passed verbatim into the message of the error.
func WithMessageTemplate ¶
func WithMessageTemplate(tmpl string) WithConstruction
WithMessageTemplate is part of the system for constructing an error with the serum.Error function.
WithMessageTemplate describes how to produce a message for the error, and allows values from the error's details to be incorporated into the message smoothly.
The templates used are very simple: "{{x}}" will look up the detail labelled "x" and place the corresponding value in that position in the string. The templates will never error; if a detail is not found with the given label, then the output will just contain the template syntax and missing label (e.g., "{{x}}" will be emitted as output).
See the examples of the Error function for complete demonstrations of usage.