Documentation
¶
Overview ¶
Package errorchan personifies Go errors as an anime character who reacts to your code failing. The name is wordplay that finally lands in Go: "-chan", the cutesy Japanese honorific, and "chan", the channel.
Errors get a personality mode that changes how they are delivered, while the real error information stays intact and debuggable. The persona only ever wraps around an error; it never hides or mangles it.
Modes ¶
Three modes shape the delivery (the underlying error is identical in each):
- ModeDere (default): sweet, flustered, apologetic, takes the blame.
- ModeTsun: annoyed at you, blames your code, grudgingly helpful anyway.
- ModeYan: unsettlingly affectionate about your failures. Do not use in production.
The current global default is read with Mode and set with SetMode; both are safe for concurrent use.
Preserving the error ¶
Every StyledError keeps the original message verbatim, appended after a clear separator on a single line, and implements Unwrap so that errors.Is, errors.As, and errors.Unwrap keep working through the persona:
styled := errorchan.Wrap(err, errorchan.WithMode(errorchan.ModeTsun)) errors.Is(styled, io.EOF) // still true styled.Original // the untouched original error styled.OriginalMessage // err.Error(), captured verbatim
Surfaces ¶
The personality can be applied through several surfaces that all share one styling core: Wrap for a single error, Styled to restyle every error flowing through a channel (the pun), Recover to restyle a recovered panic inside a deferred call, and NewSlogHandler to style error-valued slog attributes.
Hiding the Go type ¶
By default the framing names the wrapped error's Go type (for example *errors.errorString*) to aid debugging. Pass WithoutType to replace that slot with a neutral noun, run through the same phonetic transform as the rest of the framing, for cases where the framing is shown to end users:
styled := errorchan.Wrap(err, errorchan.WithoutType()) styled.OriginalMessage // still the verbatim message errors.Is(styled, io.EOF) // still works
Determinism ¶
All randomness (intro choice, kaomoji choice, stutter placement) flows through an injectable source. Pass WithSeed or WithRand for fully reproducible output, which is what the test suite relies on.
Index ¶
- Constants
- func Mode() string
- func NewSlogHandler(base slog.Handler, opts ...Option) slog.Handler
- func Recover(errp *error, opts ...Option)
- func SetMode(mode string) error
- func Styled(in <-chan error, opts ...Option) <-chan error
- func Uwuify(s string, opts ...Option) string
- type Option
- type StyledError
Examples ¶
Constants ¶
const ( // ModeDere is the default: sweet, flustered, apologetic, and takes the // blame for the failure. ModeDere = "dere" // ModeTsun is tsundere: annoyed at you, blames your code, and is grudgingly // helpful anyway. ModeTsun = "tsun" // ModeYan is yandere: unsettlingly affectionate about your failures. Do not // use in production. ModeYan = "yan" )
The supported personality modes. Pass them to SetMode or WithMode.
Variables ¶
This section is empty.
Functions ¶
func Mode ¶
func Mode() string
Mode returns the current global default mode. It is safe to call from multiple goroutines.
func NewSlogHandler ¶
NewSlogHandler wraps base so that any error-valued attribute in a log record is restyled with the configured mode before being passed on. Non-error attributes are left untouched, and attributes nested in groups are styled recursively.
Because each error is styled independently, pass WithSeed for reproducible output. Avoid WithRand here: a handler may be invoked from many goroutines at once, and a shared *rand.Rand is not safe for concurrent use.
func Recover ¶
Recover restyles a recovered panic into errp. Call it deferred at the top of a function that uses a named error return:
func doThing() (err error) {
defer errorchan.Recover(&err)
// ... code that may panic ...
}
If no panic is in flight Recover does nothing and leaves errp untouched. On a panic it recovers the value, converts it to an error (a panicked error is used directly so its chain and sentinel identity survive), styles it, and stores it through errp. A nil errp simply swallows the panic.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/klobucar/go-errorchan"
)
func main() {
doThing := func() (err error) {
defer errorchan.Recover(&err, errorchan.WithMode(errorchan.ModeTsun), errorchan.WithSeed(2))
panic(errors.New("nil pointer somewhere"))
}
err := doThing()
fmt.Println(err)
}
Output: a *errors.errorString*?! sewiouswy? I'm nyot hewping because I wike you ow anything ヽ(`Д´)ノ — nil pointer somewhere
func SetMode ¶
SetMode sets the global default mode used when no WithMode option is given. It returns an error for an unknown mode and leaves the current mode unchanged in that case. It is safe to call from multiple goroutines.
Example ¶
package main
import (
"fmt"
"github.com/klobucar/go-errorchan"
)
func main() {
// Set the global default; subsequent Wrap calls use it unless overridden.
previous := errorchan.Mode()
defer func() { _ = errorchan.SetMode(previous) }()
if err := errorchan.SetMode(errorchan.ModeYan); err != nil {
fmt.Println(err)
return
}
fmt.Println("mode is now:", errorchan.Mode())
}
Output: mode is now: yan
func Styled ¶
Styled is the pun: it restyles every error flowing through a channel. It reads errors from in, wraps each non-nil error in the configured mode, and forwards the result on the returned channel. The output channel is closed once in is drained and closed.
A single random source is resolved once and shared across the stream, so with WithSeed or WithRand the sequence of styled errors is reproducible while still varying error-to-error. nil values are forwarded unchanged (a nil error stays a nil error, never a non-nil wrapper around nil).
Example ¶
package main
import (
"errors"
"fmt"
"github.com/klobucar/go-errorchan"
)
func main() {
in := make(chan error, 2)
in <- errors.New("timeout")
in <- errors.New("refused")
close(in)
for err := range errorchan.Styled(in, errorchan.WithMode(errorchan.ModeDere), errorchan.WithSeed(5)) {
fmt.Println(err)
}
}
Output: a *errors.errorString* came o-out... I-I didn't mean to wet it happen (。•́︿•̀。) — timeout s-snyiffwe... a *errors.errorString*... I'l-ww twy hawdew n-nyext t-time, p-pwomise (。•́︿•̀。) — refused
func Uwuify ¶
Uwuify applies the raw phonetic transform to s and returns the result, for example "really cool" becomes "weawwy coow". The active mode selects the transform intensity (heavy for ModeDere and ModeYan, light for ModeTsun); pass WithSeed or WithRand for deterministic output.
Example ¶
package main
import (
"fmt"
"github.com/klobucar/go-errorchan"
)
func main() {
// WithSeed makes the phonetic transform reproducible.
fmt.Println(errorchan.Uwuify("really cool", errorchan.WithSeed(1)))
}
Output: weawwy coow
Types ¶
type Option ¶
type Option func(*config)
Option configures a styling operation. Options are applied with the functional-options pattern and are accepted by every public surface (Uwuify, Wrap, Styled, Recover, NewSlogHandler).
func WithMode ¶
WithMode selects the personality mode for this operation, overriding the global default from Mode. An unrecognized mode falls back to ModeDere.
func WithRand ¶
WithRand makes the output deterministic by using the supplied source. Unlike WithSeed, the same *rand.Rand is shared by every operation that resolves this option; callers are responsible for not using it concurrently from multiple goroutines.
func WithSeed ¶
WithSeed makes the output deterministic by deriving a fresh random source from seed. Each operation that resolves this option gets its own source, so it is safe to reuse the same seeded option across goroutines.
func WithoutType ¶
func WithoutType() Option
WithoutType suppresses the error's Go type in the persona framing. Normally the framing names the wrapped type (for example *errors.errorString*) to aid debugging; with this option the type slot is replaced by a neutral noun that is run through the same phonetic transform as the rest of the framing. The wrapped error and its verbatim message are unaffected. Useful when the framing is shown to end users who should not see Go internals.
type StyledError ¶
type StyledError struct {
// Original is the untouched error that was wrapped.
Original error
// OriginalMessage is Original.Error() captured at wrap time, preserved
// verbatim.
OriginalMessage string
// contains filtered or unexported fields
}
StyledError wraps an error with personality framing while keeping the original error fully intact and reachable. It implements error and Unwrap, so errors.Is, errors.As, and errors.Unwrap all see through to the wrapped error.
func Wrap ¶
func Wrap(err error, opts ...Option) *StyledError
Wrap returns err styled in the configured mode, or nil if err is nil. The returned *StyledError unwraps to err, so sentinel and type checks against the result keep working.
Example ¶
package main
import (
"errors"
"fmt"
"io"
"github.com/klobucar/go-errorchan"
)
func main() {
styled := errorchan.Wrap(io.EOF, errorchan.WithMode(errorchan.ModeTsun), errorchan.WithSeed(1))
// The persona wraps the error, but the original is fully intact.
fmt.Println(styled)
fmt.Println("errors.Is(styled, io.EOF):", errors.Is(styled, io.EOF))
fmt.Println("original:", styled.OriginalMessage)
}
Output: tch, a *errors.errorString*. finye, hewe's the detaiw. don't make me wepeat it >:( — EOF errors.Is(styled, io.EOF): true original: EOF
Example (Dere) ¶
package main
import (
"errors"
"fmt"
"github.com/klobucar/go-errorchan"
)
func main() {
err := errorchan.Wrap(errors.New("connection refused"), errorchan.WithSeed(7))
fmt.Println(err)
}
Output: evewything was finye u-untiw this *errors.errorString*... g-gomen nyasai >_< — connection refused
func (*StyledError) Error ¶
func (e *StyledError) Error() string
Error returns the persona framing followed by the original message, separated by [separator] on a single line. The original message is preserved verbatim.
func (*StyledError) Framing ¶
func (e *StyledError) Framing() string
Framing returns just the persona framing (intro plus kaomoji) without the original message, which is occasionally handy for custom formatting.
func (*StyledError) Mode ¶
func (e *StyledError) Mode() string
Mode reports the personality mode used to render this error.
func (*StyledError) Unwrap ¶
func (e *StyledError) Unwrap() error
Unwrap returns the original wrapped error, keeping %w-style chains and errors.Is/errors.As working through the persona.