1package fantasy
2
3import (
4 "fmt"
5 "net/http"
6 "strings"
7
8 "github.com/charmbracelet/x/exp/slice"
9)
10
11// Error is a custom error type for the fantasy package.
12type Error struct {
13 Message string
14 Title string
15 Cause error
16}
17
18func (err *Error) Error() string {
19 if err.Title == "" {
20 return err.Message
21 }
22 return fmt.Sprintf("%s: %s", err.Title, err.Message)
23}
24
25func (err Error) Unwrap() error {
26 return err.Cause
27}
28
29// ProviderError represents an error returned by an external provider.
30type ProviderError struct {
31 Message string
32 Title string
33 Cause error
34
35 URL string
36 StatusCode int
37 RequestBody []byte
38 ResponseHeaders map[string]string
39 ResponseBody []byte
40}
41
42func (m *ProviderError) Error() string {
43 if m.Title == "" {
44 return m.Message
45 }
46 return fmt.Sprintf("%s: %s", m.Title, m.Message)
47}
48
49// IsRetryable checks if the error is retryable based on the status code.
50func (m *ProviderError) IsRetryable() bool {
51 return m.StatusCode == http.StatusRequestTimeout || m.StatusCode == http.StatusConflict || m.StatusCode == http.StatusTooManyRequests
52}
53
54// RetryError represents an error that occurred during retry operations.
55type RetryError struct {
56 Errors []error
57}
58
59func (e *RetryError) Error() string {
60 if err, ok := slice.Last(e.Errors); ok {
61 return fmt.Sprintf("retry error: %v", err)
62 }
63 return "retry error: no underlying errors"
64}
65
66func (e RetryError) Unwrap() error {
67 if err, ok := slice.Last(e.Errors); ok {
68 return err
69 }
70 return nil
71}
72
73// ErrorTitleForStatusCode returns a human-readable title for a given HTTP status code.
74func ErrorTitleForStatusCode(statusCode int) string {
75 return strings.ToLower(http.StatusText(statusCode))
76}