diff --git a/util/vizerror/vizerror.go b/util/vizerror/vizerror.go new file mode 100644 index 000000000..2e0713847 --- /dev/null +++ b/util/vizerror/vizerror.go @@ -0,0 +1,24 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +// Package vizerror provides types and utility funcs for handling visible errors +// that are safe to display to end users. +package vizerror + +import ( + "errors" + "fmt" +) + +// Error is an error that is safe to display to end users. +type Error error + +// New returns an error that formats as the given text. +func New(text string) error { + return Error(errors.New(text)) +} + +// Errorf returns an error with the specified format and values. +func Errorf(format string, a ...any) error { + return Error(fmt.Errorf(format, a...)) +} diff --git a/util/vizerror/vizerror_test.go b/util/vizerror/vizerror_test.go new file mode 100644 index 000000000..c62ab53df --- /dev/null +++ b/util/vizerror/vizerror_test.go @@ -0,0 +1,30 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package vizerror + +import ( + "errors" + "io/fs" + "testing" +) + +func TestNew(t *testing.T) { + err := New("abc") + if err.Error() != "abc" { + t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc") + } +} + +func TestErrorf(t *testing.T) { + err := Errorf("%w", fs.ErrNotExist) + + if got, want := err.Error(), "file does not exist"; got != want { + t.Errorf("Errorf().Error() = %q, want %q", got, want) + } + + // ensure error wrapping still works + if !errors.Is(err, fs.ErrNotExist) { + t.Errorf("error chain does not contain fs.ErrNotExist") + } +}