Skip to content

Commit

Permalink
Remove useless contextNoBodyImpl type
Browse files Browse the repository at this point in the history
  • Loading branch information
EwenQuim committed Dec 19, 2024
1 parent 16158a8 commit 2d3fbb8
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 114 deletions.
111 changes: 41 additions & 70 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ContextNoBody = ContextWithBody[any]
// Please do not use a pointer type as parameter.
type ContextWithBody[B any] interface {
context.Context

// Body returns the body of the request.
// If (*B) implements [InTransformer], it will be transformed after deserialization.
// It caches the result, so it can be called multiple times.
Expand Down Expand Up @@ -94,17 +95,9 @@ type ContextWithBody[B any] interface {
Redirect(code int, url string) (any, error)
}

// NewContextWithBody returns a new context. It is used internally by Fuego. You probably want to use Ctx[B] instead.
func NewContextWithBody[B any](w http.ResponseWriter, r *http.Request, options readOptions) ContextWithBody[B] {
c := &contextWithBodyImpl[B]{
contextNoBodyImpl: NewContextNoBody(w, r, options),
}

return c
}

func NewContextNoBody(w http.ResponseWriter, r *http.Request, options readOptions) contextNoBodyImpl {
c := contextNoBodyImpl{
// NewNetHTTPContext returns a new context. It is used internally by Fuego. You probably want to use Ctx[B] instead.
func NewNetHTTPContext[B any](w http.ResponseWriter, r *http.Request, options readOptions) ContextWithBody[B] {
c := &netHttpContext[B]{
Res: w,
Req: r,
readOptions: readOptions{
Expand All @@ -113,25 +106,21 @@ func NewContextNoBody(w http.ResponseWriter, r *http.Request, options readOption
},
urlValues: r.URL.Query(),
}

return c
}

var (
_ ContextWithBody[any] = &netHttpContext[any]{} // Check that ContextWithBody implements Ctx.
_ ContextWithBody[string] = &netHttpContext[string]{} // Check that ContextWithBody implements Ctx.
)

// ContextWithBody is the same as fuego.ContextNoBody, but
// has a Body. The Body type parameter represents the expected data type
// from http.Request.Body. Please do not use a pointer as a type parameter.
type contextWithBodyImpl[Body any] struct {
type netHttpContext[Body any] struct {
body *Body // Cache the body in request context, because it is not possible to read an HTTP request body multiple times.
contextNoBodyImpl
}

var (
_ ContextWithBody[any] = &contextWithBodyImpl[any]{} // Check that ContextWithBody implements Ctx.
_ ContextWithBody[string] = &contextWithBodyImpl[string]{} // Check that ContextWithBody implements Ctx.
)

// ContextNoBody is used when the controller does not have a body.
// It is used as a base context for other Context types.
type contextNoBodyImpl struct {
Req *http.Request
Res http.ResponseWriter

Expand All @@ -144,27 +133,9 @@ type contextNoBodyImpl struct {
readOptions readOptions
}

var (
_ ContextWithBody[any] = contextNoBodyImpl{} // Check that ContextNoBody implements Ctx.
_ context.Context = contextNoBodyImpl{} // Check that ContextNoBody implements context.Context.
)

func (c contextNoBodyImpl) Body() (any, error) {
slog.Warn("this method should not be called. It probably happened because you passed the context to another controller.")
return body[map[string]any](c)
}

func (c contextNoBodyImpl) MustBody() any {
b, err := c.Body()
if err != nil {
panic(err)
}
return b
}

// SetStatus sets the status code of the response.
// Alias to http.ResponseWriter.WriteHeader.
func (c contextNoBodyImpl) SetStatus(code int) {
func (c netHttpContext[B]) SetStatus(code int) {
c.Res.WriteHeader(code)
}

Expand All @@ -175,65 +146,65 @@ type readOptions struct {
LogBody bool
}

func (c contextNoBodyImpl) Redirect(code int, url string) (any, error) {
func (c netHttpContext[B]) Redirect(code int, url string) (any, error) {
http.Redirect(c.Res, c.Req, url, code)

return nil, nil
}

// ContextNoBody implements the context interface via [net/http.Request.Context]
func (c contextNoBodyImpl) Deadline() (deadline time.Time, ok bool) {
func (c netHttpContext[B]) Deadline() (deadline time.Time, ok bool) {
return c.Req.Context().Deadline()
}

// ContextNoBody implements the context interface via [net/http.Request.Context]
func (c contextNoBodyImpl) Done() <-chan struct{} {
func (c netHttpContext[B]) Done() <-chan struct{} {
return c.Req.Context().Done()
}

// ContextNoBody implements the context interface via [net/http.Request.Context]
func (c contextNoBodyImpl) Err() error {
func (c netHttpContext[B]) Err() error {
return c.Req.Context().Err()
}

// ContextNoBody implements the context interface via [net/http.Request.Context]
func (c contextNoBodyImpl) Value(key any) any {
func (c netHttpContext[B]) Value(key any) any {
return c.Req.Context().Value(key)
}

// ContextNoBody implements the context interface via [net/http.Request.Context]
func (c contextNoBodyImpl) Context() context.Context {
func (c netHttpContext[B]) Context() context.Context {
return c.Req.Context()
}

// Get request header
func (c contextNoBodyImpl) Header(key string) string {
func (c netHttpContext[B]) Header(key string) string {
return c.Request().Header.Get(key)
}

// Has request header
func (c contextNoBodyImpl) HasHeader(key string) bool {
func (c netHttpContext[B]) HasHeader(key string) bool {
return c.Header(key) != ""
}

// Sets response header
func (c contextNoBodyImpl) SetHeader(key, value string) {
func (c netHttpContext[B]) SetHeader(key, value string) {
c.Response().Header().Set(key, value)
}

// Get request cookie
func (c contextNoBodyImpl) Cookie(name string) (*http.Cookie, error) {
func (c netHttpContext[B]) Cookie(name string) (*http.Cookie, error) {
return c.Request().Cookie(name)
}

// Has request cookie
func (c contextNoBodyImpl) HasCookie(name string) bool {
func (c netHttpContext[B]) HasCookie(name string) bool {
_, err := c.Cookie(name)
return err == nil
}

// Sets response cookie
func (c contextNoBodyImpl) SetCookie(cookie http.Cookie) {
func (c netHttpContext[B]) SetCookie(cookie http.Cookie) {
http.SetCookie(c.Response(), &cookie)
}

Expand All @@ -245,7 +216,7 @@ func (c contextNoBodyImpl) SetCookie(cookie http.Cookie) {
// that the templates will be parsed only once, removing
// the need to parse the templates on each request but also preventing
// to dynamically use new templates.
func (c contextNoBodyImpl) Render(templateToExecute string, data any, layoutsGlobs ...string) (CtxRenderer, error) {
func (c netHttpContext[B]) Render(templateToExecute string, data any, layoutsGlobs ...string) (CtxRenderer, error) {
return &StdRenderer{
templateToExecute: templateToExecute,
templates: c.templates,
Expand All @@ -256,7 +227,7 @@ func (c contextNoBodyImpl) Render(templateToExecute string, data any, layoutsGlo
}

// PathParams returns the path parameters of the request.
func (c contextNoBodyImpl) PathParam(name string) string {
func (c netHttpContext[B]) PathParam(name string) string {
return c.Req.PathValue(name)
}

Expand All @@ -280,12 +251,12 @@ func (e QueryParamInvalidTypeError) Error() string {
}

// QueryParams returns the query parameters of the request. It is a shortcut for c.Req.URL.Query().
func (c contextNoBodyImpl) QueryParams() url.Values {
func (c netHttpContext[B]) QueryParams() url.Values {
return c.urlValues
}

// QueryParamsArr returns an slice of string from the given query parameter.
func (c contextNoBodyImpl) QueryParamArr(name string) []string {
func (c netHttpContext[B]) QueryParamArr(name string) []string {
_, ok := c.params[name]
if !ok {
slog.Warn("query parameter not expected in OpenAPI spec", "param", name)
Expand All @@ -301,7 +272,7 @@ func (c contextNoBodyImpl) QueryParamArr(name string) []string {
// fuego.Get(s, "/test", myController,
// option.Query("name", "Name", param.Default("hey"))
// )
func (c contextNoBodyImpl) QueryParam(name string) string {
func (c netHttpContext[B]) QueryParam(name string) string {
_, ok := c.params[name]
if !ok {
slog.Warn("query parameter not expected in OpenAPI spec", "param", name, "expected_one_of", c.params)
Expand All @@ -314,7 +285,7 @@ func (c contextNoBodyImpl) QueryParam(name string) string {
return c.urlValues.Get(name)
}

func (c contextNoBodyImpl) QueryParamIntErr(name string) (int, error) {
func (c netHttpContext[B]) QueryParamIntErr(name string) (int, error) {
param := c.QueryParam(name)
if param == "" {
defaultValue, ok := c.params[name].Default.(int)
Expand Down Expand Up @@ -348,7 +319,7 @@ func (c contextNoBodyImpl) QueryParamIntErr(name string) (int, error) {
//
// and the query parameter does not exist, it will return 1.
// If the query parameter does not exist and there is no default value, or if it is not an int, it returns 0.
func (c contextNoBodyImpl) QueryParamInt(name string) int {
func (c netHttpContext[B]) QueryParamInt(name string) int {
param, err := c.QueryParamIntErr(name)
if err != nil {
return 0
Expand All @@ -367,7 +338,7 @@ func (c contextNoBodyImpl) QueryParamInt(name string) int {
//
// and the query parameter does not exist in the HTTP request, it will return true.
// Accepted values are defined as [strconv.ParseBool]
func (c contextNoBodyImpl) QueryParamBoolErr(name string) (bool, error) {
func (c netHttpContext[B]) QueryParamBoolErr(name string) (bool, error) {
param := c.QueryParam(name)
if param == "" {
defaultValue, ok := c.params[name].Default.(bool)
Expand Down Expand Up @@ -400,7 +371,7 @@ func (c contextNoBodyImpl) QueryParamBoolErr(name string) (bool, error) {
// )
//
// and the query parameter does not exist in the HTTP request, it will return true.
func (c contextNoBodyImpl) QueryParamBool(name string) bool {
func (c netHttpContext[B]) QueryParamBool(name string) bool {
param, err := c.QueryParamBoolErr(name)
if err != nil {
return false
Expand All @@ -409,26 +380,26 @@ func (c contextNoBodyImpl) QueryParamBool(name string) bool {
return param
}

func (c contextNoBodyImpl) MainLang() string {
func (c netHttpContext[B]) MainLang() string {
return strings.Split(c.MainLocale(), "-")[0]
}

func (c contextNoBodyImpl) MainLocale() string {
func (c netHttpContext[B]) MainLocale() string {
return strings.Split(c.Req.Header.Get("Accept-Language"), ",")[0]
}

// Request returns the HTTP request.
func (c contextNoBodyImpl) Request() *http.Request {
func (c netHttpContext[B]) Request() *http.Request {
return c.Req
}

// Response returns the HTTP response writer.
func (c contextNoBodyImpl) Response() http.ResponseWriter {
func (c netHttpContext[B]) Response() http.ResponseWriter {
return c.Res
}

// MustBody works like Body, but panics if there is an error.
func (c *contextWithBodyImpl[B]) MustBody() B {
func (c *netHttpContext[B]) MustBody() B {
b, err := c.Body()
if err != nil {
panic(err)
Expand All @@ -441,17 +412,17 @@ func (c *contextWithBodyImpl[B]) MustBody() B {
// It caches the result, so it can be called multiple times.
// The reason the body is cached is that it is impossible to read an HTTP request body multiple times, not because of performance.
// For decoding, it uses the Content-Type header. If it is not set, defaults to application/json.
func (c *contextWithBodyImpl[B]) Body() (B, error) {
func (c *netHttpContext[B]) Body() (B, error) {
if c.body != nil {
return *c.body, nil
}

body, err := body[B](c.contextNoBodyImpl)
body, err := body[B](*c)
c.body = &body
return body, err
}

func body[B any](c contextNoBodyImpl) (B, error) {
func body[B any](c netHttpContext[B]) (B, error) {
// Limit the size of the request body.
if c.readOptions.MaxBodySize != 0 {
c.Req.Body = http.MaxBytesReader(nil, c.Req.Body, c.readOptions.MaxBodySize)
Expand Down
Loading

0 comments on commit 2d3fbb8

Please sign in to comment.