From 6ed38cf1cf53b014de91f8eb78015d6c612761d3 Mon Sep 17 00:00:00 2001 From: "Gustavo \"Guz\" L de Mello" Date: Tue, 29 Jul 2025 19:15:10 -0300 Subject: [PATCH] refactor(smalltrip,problems): define RetryAfter header parsing into separated type --- smalltrip/problem/400.go | 67 +----------------- smalltrip/problem/500.go | 130 ++--------------------------------- smalltrip/problem/headers.go | 33 +++++++++ 3 files changed, 39 insertions(+), 191 deletions(-) create mode 100644 smalltrip/problem/headers.go diff --git a/smalltrip/problem/400.go b/smalltrip/problem/400.go index 396f372..45e1924 100644 --- a/smalltrip/problem/400.go +++ b/smalltrip/problem/400.go @@ -1,9 +1,6 @@ package problem import ( - "encoding/json" - "encoding/xml" - "errors" "fmt" "net/http" "strings" @@ -304,71 +301,11 @@ func NewTooManyRequests[T time.Time | time.Duration](retryAfter T, opts ...Optio type TooManyRequests[T time.Time | time.Duration] struct { Problem - RetryAfter T `json:"retryAfter" xml:"retry-after"` -} - -var ( - _ json.Marshaler = TooManyRequests[time.Time]{} - _ xml.Marshaler = TooManyRequests[time.Time]{} -) - -func (p TooManyRequests[T]) MarshalJSON() ([]byte, error) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return json.Marshal(struct { - Problem - RetryAfter string `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return json.Marshal(struct { - Problem - RetryAfter int `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return nil, errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } -} - -func (p TooManyRequests[T]) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return e.Encode(struct { - Problem - RetryAfter string `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return e.Encode(struct { - Problem - RetryAfter int `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } + RetryAfter RetryAfter[T] `json:"retryAfter" xml:"retry-after"` } func (p TooManyRequests[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - if !t.IsZero() { - w.Header().Set("Retry-After", t.Format(http.TimeFormat)) - } - case time.Duration: - if t != 0 { - w.Header().Set("Retry-After", fmt.Sprintf("%.0f", t.Seconds())) - } - } + w.Header().Set("Retry-After", p.RetryAfter.String()) p.handler(p).ServeHTTP(w, r) } diff --git a/smalltrip/problem/500.go b/smalltrip/problem/500.go index c1e6e9c..cabd32a 100644 --- a/smalltrip/problem/500.go +++ b/smalltrip/problem/500.go @@ -1,10 +1,8 @@ package problem import ( - "encoding/json" "encoding/xml" "errors" - "fmt" "net/http" "time" ) @@ -67,71 +65,11 @@ func NewNotImplemented[T time.Time | time.Duration](retryAfter T, opts ...Option type NotImplemented[T time.Time | time.Duration] struct { Problem - RetryAfter T `json:"retryAfter" xml:"retry-after"` -} - -var ( - _ json.Marshaler = NotImplemented[time.Time]{} - _ xml.Marshaler = NotImplemented[time.Time]{} -) - -func (p NotImplemented[T]) MarshalJSON() ([]byte, error) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return json.Marshal(struct { - Problem - RetryAfter string `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return json.Marshal(struct { - Problem - RetryAfter int `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return nil, errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } -} - -func (p NotImplemented[T]) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return e.Encode(struct { - Problem - RetryAfter string `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return e.Encode(struct { - Problem - RetryAfter int `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } + RetryAfter RetryAfter[T] `json:"retryAfter" xml:"retry-after"` } func (p NotImplemented[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - if !t.IsZero() { - w.Header().Set("Retry-After", t.Format(http.TimeFormat)) - } - case time.Duration: - if t != 0 { - w.Header().Set("Retry-After", fmt.Sprintf("%.0f", t.Seconds())) - } - } + w.Header().Set("Retry-After", p.RetryAfter.String()) p.handler(p).ServeHTTP(w, r) } @@ -148,71 +86,11 @@ func NewServiceUnavailable[T time.Time | time.Duration](retryAfter T, opts ...Op type ServiceUnavailable[T time.Time | time.Duration] struct { Problem - RetryAfter T `json:"retryAfter" xml:"retry-after"` -} - -var ( - _ json.Marshaler = ServiceUnavailable[time.Time]{} - _ xml.Marshaler = ServiceUnavailable[time.Time]{} -) - -func (p ServiceUnavailable[T]) MarshalJSON() ([]byte, error) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return json.Marshal(struct { - Problem - RetryAfter string `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return json.Marshal(struct { - Problem - RetryAfter int `json:"retryAfter,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return nil, errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } -} - -func (p ServiceUnavailable[T]) MarshalXML(e *xml.Encoder, _ xml.StartElement) error { - switch t := any(p.RetryAfter).(type) { - case time.Time: - return e.Encode(struct { - Problem - RetryAfter string `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: t.Format(time.RFC3339), - }) - case time.Duration: - return e.Encode(struct { - Problem - RetryAfter int `xml:"retry-after,omitempty"` - }{ - Problem: p.Problem, - RetryAfter: int(t.Seconds()), - }) - default: - return errors.New("problems-not-implemented: RetryAfter is not of type time.Time or time.Duration") - } + RetryAfter RetryAfter[T] `json:"retryAfter" xml:"retry-after"` } func (p ServiceUnavailable[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { - switch t := any(p.RetryAfter).(type) { - case time.Time: - if !t.IsZero() { - w.Header().Set("Retry-After", t.Format(http.TimeFormat)) - } - case time.Duration: - if t != 0 { - w.Header().Set("Retry-After", fmt.Sprintf("%.0f", t.Seconds())) - } - } + w.Header().Set("Retry-After", p.RetryAfter.String()) p.handler(p).ServeHTTP(w, r) } diff --git a/smalltrip/problem/headers.go b/smalltrip/problem/headers.go new file mode 100644 index 0000000..aa5ee3a --- /dev/null +++ b/smalltrip/problem/headers.go @@ -0,0 +1,33 @@ +package problem + +import ( + "encoding" + "fmt" + "net/http" + "time" +) + +type RetryAfter[T time.Time | time.Duration] struct { + time T +} + +var ( + _ encoding.TextMarshaler = RetryAfter[time.Time]{} + _ fmt.Stringer = RetryAfter[time.Time]{} +) + +func (h RetryAfter[T]) MarshalText() ([]byte, error) { + return []byte(h.String()), nil +} + +func (h RetryAfter[T]) String() string { + switch t := any(h.time).(type) { + case time.Time: + if !t.IsZero() { + return t.Format(http.TimeFormat) + } + case time.Duration: + return fmt.Sprintf("%.0f", t.Seconds()) + } + return "" +}