|
| 1 | +// Package errors provides custom error types for user-friendly error messaging. |
| 2 | +// |
| 3 | +// This package distinguishes between user-facing errors and technical errors, |
| 4 | +// allowing the CLI to display clean messages while preserving technical details |
| 5 | +// for debugging with verbose flags. |
| 6 | +package errors |
| 7 | + |
| 8 | +import ( |
| 9 | + "errors" |
| 10 | + "fmt" |
| 11 | +) |
| 12 | + |
| 13 | +// UserError represents an error with a user-friendly message. |
| 14 | +// |
| 15 | +// UserError separates user-facing messages from technical implementation details, |
| 16 | +// making CLI output cleaner while preserving debugging information for verbose mode. |
| 17 | +type UserError struct { |
| 18 | + // Message is the user-friendly error message displayed to users. |
| 19 | + Message string |
| 20 | + |
| 21 | + // Err is the underlying technical error, preserved for debugging |
| 22 | + // but hidden from normal output. |
| 23 | + Err error |
| 24 | + |
| 25 | + // Hint provides actionable guidance to help users resolve the issue. |
| 26 | + Hint string |
| 27 | +} |
| 28 | + |
| 29 | +// Error implements the error interface and returns the user-friendly message. |
| 30 | +// |
| 31 | +// If a hint is set, it appends the hint to the message on a new line. |
| 32 | +func (e *UserError) Error() string { |
| 33 | + if e.Hint != "" { |
| 34 | + return fmt.Sprintf("%s\n%s", e.Message, e.Hint) |
| 35 | + } |
| 36 | + return e.Message |
| 37 | +} |
| 38 | + |
| 39 | +// Unwrap returns the underlying technical error for error chain inspection. |
| 40 | +func (e *UserError) Unwrap() error { |
| 41 | + return e.Err |
| 42 | +} |
| 43 | + |
| 44 | +// IsUserError checks whether an error chain contains a UserError. |
| 45 | +// |
| 46 | +// It uses errors.As to walk the error chain and returns the first UserError found. |
| 47 | +// The second return value indicates whether a UserError was found. |
| 48 | +func IsUserError(err error) (*UserError, bool) { |
| 49 | + var userErr *UserError |
| 50 | + if errors.As(err, &userErr) { |
| 51 | + return userErr, true |
| 52 | + } |
| 53 | + return nil, false |
| 54 | +} |
| 55 | + |
| 56 | +// NewUserError creates a user-facing error with a message. |
| 57 | +// |
| 58 | +// Use this for simple errors that don't need hints or wrapped technical errors. |
| 59 | +func NewUserError(message string) *UserError { |
| 60 | + return &UserError{Message: message} |
| 61 | +} |
| 62 | + |
| 63 | +// NewUserErrorWithHint creates a user-facing error with a message and actionable hint. |
| 64 | +// |
| 65 | +// The hint should provide specific instructions to help users resolve the issue, |
| 66 | +// such as command examples or documentation links. |
| 67 | +func NewUserErrorWithHint(message, hint string) *UserError { |
| 68 | + return &UserError{Message: message, Hint: hint} |
| 69 | +} |
| 70 | + |
| 71 | +// WrapUserError wraps a technical error with a user-friendly message. |
| 72 | +// |
| 73 | +// The technical error is preserved for debugging with verbose flags but hidden |
| 74 | +// from normal output. |
| 75 | +func WrapUserError(message string, err error) *UserError { |
| 76 | + return &UserError{Message: message, Err: err} |
| 77 | +} |
| 78 | + |
| 79 | +// WrapUserErrorWithHint wraps a technical error with a user-friendly message and hint. |
| 80 | +// |
| 81 | +// This combines a clean user message, actionable guidance, and technical details |
| 82 | +// for debugging. |
| 83 | +func WrapUserErrorWithHint(message, hint string, err error) *UserError { |
| 84 | + return &UserError{Message: message, Hint: hint, Err: err} |
| 85 | +} |
0 commit comments