// Package smtp provides email sending functionality package smtp import ( "errors" "fmt" ) // Common SMTP error types var ( // ErrInvalidConfig indicates missing or invalid SMTP configuration ErrInvalidConfig = errors.New("invalid SMTP configuration") // ErrInvalidRecipient indicates an invalid or empty recipient email ErrInvalidRecipient = errors.New("invalid recipient email") // ErrTemplateParsingFailed indicates a failure to parse an email template ErrTemplateParsingFailed = errors.New("failed to parse email template") // ErrTemplateExecutionFailed indicates a failure to execute an email template ErrTemplateExecutionFailed = errors.New("failed to execute email template") // ErrMessageCreationFailed indicates a failure to create an email message ErrMessageCreationFailed = errors.New("failed to create email message") // ErrSendFailed indicates a failure to send an email ErrSendFailed = errors.New("failed to send email") // ErrConnectionFailed indicates a failure to connect to the SMTP server ErrConnectionFailed = errors.New("failed to connect to SMTP server") // ErrInvalidTemplate indicates missing or invalid template data ErrInvalidTemplate = errors.New("invalid template data") // ErrClientClosed indicates the SMTP client is already closed ErrClientClosed = errors.New("SMTP client is closed") ) // Error represents an SMTP-specific error with detailed context type Error struct { // Op is the operation that failed (e.g., "send", "connect", "template") Op string // Err is the underlying error Err error // Details contains additional error context Details string // Template indicates which email template was involved (if applicable) Template string // Recipient indicates which email address was involved (if applicable) Recipient string } // Error returns a string representation of the error func (e *Error) Error() string { msg := fmt.Sprintf("smtp %s failed", e.Op) if e.Template != "" { msg = fmt.Sprintf("%s for template %s", msg, e.Template) } if e.Recipient != "" { msg = fmt.Sprintf("%s to %s", msg, e.Recipient) } if e.Details != "" { msg = fmt.Sprintf("%s: %s", msg, e.Details) } if e.Err != nil { msg = fmt.Sprintf("%s: %v", msg, e.Err) } return msg } // Unwrap returns the underlying error func (e *Error) Unwrap() error { return e.Err } // Is reports whether target matches this error func (e *Error) Is(target error) bool { return errors.Is(e.Err, target) } // NewError creates a new SMTP error func NewError(op string, err error, details string) *Error { return &Error{ Op: op, Err: err, Details: details, } } // NewTemplateError creates a new SMTP template error func NewTemplateError(op string, templateName string, err error, details string) *Error { return &Error{ Op: op, Template: templateName, Err: err, Details: details, } } // NewSendError creates a new SMTP send error func NewSendError(op string, recipient string, err error, details string) *Error { return &Error{ Op: op, Recipient: recipient, Err: err, Details: details, } } // IsConnectionError returns true if the error indicates a connection problem func IsConnectionError(err error) bool { var e *Error return errors.As(err, &e) && errors.Is(e.Err, ErrConnectionFailed) } // IsTemplateError returns true if the error is related to template processing func IsTemplateError(err error) bool { var e *Error return errors.As(err, &e) && (errors.Is(e.Err, ErrTemplateParsingFailed) || errors.Is(e.Err, ErrTemplateExecutionFailed) || errors.Is(e.Err, ErrInvalidTemplate)) }