refactor(smalltrip,problems): define RetryAfter header parsing into separated type

This commit is contained in:
Guz
2025-07-29 19:15:10 -03:00
parent 42f2f114fa
commit 6ed38cf1cf
3 changed files with 39 additions and 191 deletions

View File

@@ -1,9 +1,6 @@
package problem package problem
import ( import (
"encoding/json"
"encoding/xml"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
@@ -304,71 +301,11 @@ func NewTooManyRequests[T time.Time | time.Duration](retryAfter T, opts ...Optio
type TooManyRequests[T time.Time | time.Duration] struct { type TooManyRequests[T time.Time | time.Duration] struct {
Problem Problem
RetryAfter T `json:"retryAfter" xml:"retry-after"` RetryAfter 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")
}
} }
func (p TooManyRequests[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p TooManyRequests[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch t := any(p.RetryAfter).(type) { w.Header().Set("Retry-After", p.RetryAfter.String())
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()))
}
}
p.handler(p).ServeHTTP(w, r) p.handler(p).ServeHTTP(w, r)
} }

View File

@@ -1,10 +1,8 @@
package problem package problem
import ( import (
"encoding/json"
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt"
"net/http" "net/http"
"time" "time"
) )
@@ -67,71 +65,11 @@ func NewNotImplemented[T time.Time | time.Duration](retryAfter T, opts ...Option
type NotImplemented[T time.Time | time.Duration] struct { type NotImplemented[T time.Time | time.Duration] struct {
Problem Problem
RetryAfter T `json:"retryAfter" xml:"retry-after"` RetryAfter 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")
}
} }
func (p NotImplemented[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p NotImplemented[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch t := any(p.RetryAfter).(type) { w.Header().Set("Retry-After", p.RetryAfter.String())
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()))
}
}
p.handler(p).ServeHTTP(w, r) 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 { type ServiceUnavailable[T time.Time | time.Duration] struct {
Problem Problem
RetryAfter T `json:"retryAfter" xml:"retry-after"` RetryAfter 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")
}
} }
func (p ServiceUnavailable[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p ServiceUnavailable[T]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch t := any(p.RetryAfter).(type) { w.Header().Set("Retry-After", p.RetryAfter.String())
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()))
}
}
p.handler(p).ServeHTTP(w, r) p.handler(p).ServeHTTP(w, r)
} }

View File

@@ -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 ""
}