vizerror.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package vizerror provides types and utility funcs for handling visible errors
  4. // that are safe to display to end users.
  5. package vizerror
  6. import (
  7. "errors"
  8. "fmt"
  9. )
  10. // Error is an error that is safe to display to end users.
  11. type Error struct {
  12. publicErr error // visible to end users
  13. wrapped error // internal
  14. }
  15. // Error implements the error interface. The returned string is safe to display
  16. // to end users.
  17. func (e Error) Error() string {
  18. return e.publicErr.Error()
  19. }
  20. // New returns an error that formats as the given text. It always returns a vizerror.Error.
  21. func New(publicMsg string) error {
  22. err := errors.New(publicMsg)
  23. return Error{
  24. publicErr: err,
  25. wrapped: err,
  26. }
  27. }
  28. // Errorf returns an Error with the specified publicMsgFormat and values. It always returns a vizerror.Error.
  29. //
  30. // Warning: avoid using an error as one of the format arguments, as this will cause the text
  31. // of that error to be displayed to the end user (which is probably not what you want).
  32. func Errorf(publicMsgFormat string, a ...any) error {
  33. err := fmt.Errorf(publicMsgFormat, a...)
  34. return Error{
  35. publicErr: err,
  36. wrapped: err,
  37. }
  38. }
  39. // Unwrap returns the underlying error.
  40. //
  41. // If the Error was constructed using [WrapWithMessage], this is the wrapped (internal) error
  42. // and not the user-visible error message.
  43. func (e Error) Unwrap() error {
  44. return e.wrapped
  45. }
  46. // Wrap wraps publicErr with a vizerror.Error.
  47. //
  48. // Deprecated: this is almost always the wrong thing to do. Are you really sure
  49. // you know exactly what err.Error() will stringify to and be safe to show to
  50. // users? [WrapWithMessage] is probably what you want.
  51. func Wrap(publicErr error) error {
  52. if publicErr == nil {
  53. return nil
  54. }
  55. return Error{publicErr: publicErr, wrapped: publicErr}
  56. }
  57. // WrapWithMessage wraps the given error with a message that's safe to display
  58. // to end users. The text of the wrapped error will not be displayed to end
  59. // users.
  60. //
  61. // WrapWithMessage should almost always be preferred to [Wrap].
  62. func WrapWithMessage(wrapped error, publicMsg string) error {
  63. return Error{
  64. publicErr: errors.New(publicMsg),
  65. wrapped: wrapped,
  66. }
  67. }
  68. // As returns the first vizerror.Error in err's chain.
  69. func As(err error) (e Error, ok bool) {
  70. ok = errors.As(err, &e)
  71. return
  72. }