removed intermediate /internal directory

This commit is contained in:
2026-02-09 13:40:31 -07:00
parent b155280081
commit 0944703d2a
50 changed files with 117 additions and 101 deletions
+152
View File
@@ -0,0 +1,152 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
bodyRes struct {
body io.ReadCloser
res Response
}
)
var _ Response = bodyRes{}
func Body(body io.ReadCloser) Response {
return bodyRes{
body: body,
}
}
func (b bodyRes) String() string {
if b.res != nil {
return fmt.Sprintf(`{"body": %q, "nested": %s}`, b.body, b.res)
}
return fmt.Sprintf(`{"body": %q}`, b.body)
}
func (b bodyRes) wrap(res Response) Response {
b.res = res
return b
}
func (b bodyRes) Status(code int) Response {
return Status(code).wrap(b)
}
func (b bodyRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(b)
}
func (b bodyRes) HTML(body []byte) Response {
return HTML(body).wrap(b)
}
func (b bodyRes) JSON(body any) Response {
return JSON(body).wrap(b)
}
func (b bodyRes) Body(body io.ReadCloser) Response {
b.body = body
return b
}
func (b bodyRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(b)
}
func (b bodyRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(b)
}
func (b bodyRes) HXLocation(v string) Response {
return HXLocation(v).wrap(b)
}
func (b bodyRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(b)
}
func (b bodyRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(b)
}
func (b bodyRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(b)
}
func (b bodyRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(b)
}
func (b bodyRes) HXReswap(v string) Response {
return HXReswap(v).wrap(b)
}
func (b bodyRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(b)
}
func (b bodyRes) HXReselect(v string) Response {
return HXReselect(v).wrap(b)
}
func (b bodyRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(b)
}
func (b bodyRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(b)
}
func (b bodyRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(b)
}
func (b bodyRes) GetStatus() (int, bool) {
if b.res == nil {
return 0, false
}
return b.res.GetStatus()
}
func (b bodyRes) getHeaders() [][2]string {
if b.res != nil {
return b.res.getHeaders()
}
return nil
}
func (b bodyRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if b.res == nil {
return 0, "", false
}
return b.res.GetRedirect()
}
func (b bodyRes) getBody() (body io.ReadCloser, ok bool, err error) {
return b.body, true, nil
}
func (b bodyRes) getCookies() []http.Cookie {
if b.res != nil {
return b.res.getCookies()
}
return nil
}
func (b bodyRes) getContentType() (contentType string, ok bool) {
if b.res != nil {
return b.res.getContentType()
}
return "", false
}
+155
View File
@@ -0,0 +1,155 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
cookieRes struct {
cookie http.Cookie
res Response
}
)
var _ Response = cookieRes{}
func Cookie(c http.Cookie) Response {
return cookieRes{
cookie: c,
}
}
func (c cookieRes) String() string {
if c.res != nil {
return fmt.Sprintf(`{"cookie": %q, "nested": %s}`, &c.cookie, c.res)
}
return fmt.Sprintf(`{"cookie": %q}`, &c.cookie)
}
func (c cookieRes) wrap(res Response) Response {
c.res = res
return c
}
func (c cookieRes) Status(code int) Response {
return Status(code).wrap(c)
}
func (c cookieRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(c)
}
func (c cookieRes) HTML(body []byte) Response {
return HTML(body).wrap(c)
}
func (c cookieRes) JSON(body any) Response {
return JSON(body).wrap(c)
}
func (c cookieRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(c)
}
func (c cookieRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(c)
}
func (c cookieRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(c)
}
func (c cookieRes) HXLocation(v string) Response {
return HXLocation(v).wrap(c)
}
func (c cookieRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(c)
}
func (c cookieRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(c)
}
func (c cookieRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(c)
}
func (c cookieRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(c)
}
func (c cookieRes) HXReswap(v string) Response {
return HXReswap(v).wrap(c)
}
func (c cookieRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(c)
}
func (c cookieRes) HXReselect(v string) Response {
return HXReselect(v).wrap(c)
}
func (c cookieRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(c)
}
func (c cookieRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(c)
}
func (c cookieRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(c)
}
func (c cookieRes) GetStatus() (int, bool) {
if c.res == nil {
return 0, false
}
return c.res.GetStatus()
}
func (c cookieRes) getHeaders() [][2]string {
if c.res != nil {
return c.res.getHeaders()
}
return nil
}
func (c cookieRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if c.res == nil {
return 0, "", false
}
return c.res.GetRedirect()
}
func (c cookieRes) getBody() (body io.ReadCloser, ok bool, err error) {
if c.res == nil {
return nil, false, nil
}
return c.res.getBody()
}
func (c cookieRes) getCookies() []http.Cookie {
if c.res != nil {
return append(c.res.getCookies(), c.cookie)
}
return []http.Cookie{c.cookie}
}
func (c cookieRes) getContentType() (contentType string, ok bool) {
if c.res != nil {
return c.res.getContentType()
}
return "", false
}
+186
View File
@@ -0,0 +1,186 @@
package response
import (
"errors"
"fmt"
"net/http"
"strings"
"ruben/inventory2/consts"
)
type (
// ErrorResponse is an error
ErrorResponse struct {
err error
msg string
status int
html []byte
}
)
// Constructors
func Errorf(format string, args ...any) ErrorResponse {
return ErrorResponse{
err: fmt.Errorf(format, args...),
}
}
func BadRequest() ErrorResponse {
return ErrorResponse{
status: http.StatusBadRequest,
}
}
func NotFound() ErrorResponse {
return ErrorResponse{
status: http.StatusNotFound,
}
}
func Unauthorized() ErrorResponse {
return ErrorResponse{
status: http.StatusUnauthorized,
}
}
func Forbidden() ErrorResponse {
return ErrorResponse{
status: http.StatusForbidden,
}
}
func Conflict() ErrorResponse {
return ErrorResponse{
status: http.StatusConflict,
}
}
// builder pattern implementation
func (e ErrorResponse) Msg(msg string) ErrorResponse {
e.msg = msg
return e
}
func (e ErrorResponse) Msgf(format string, args ...any) ErrorResponse {
e.msg = fmt.Sprintf(format, args...)
return e
}
func (e ErrorResponse) Status(status int) ErrorResponse {
e.status = status
return e
}
func (e ErrorResponse) Wrap(err error) ErrorResponse {
e.err = err
return e
}
func (e ErrorResponse) HTML(h []byte) ErrorResponse {
e.html = h
return e
}
// error implementation
func (e ErrorResponse) Error() string {
parts := make([]string, 0, 3)
if e.msg != "" {
parts = append(parts, e.msg)
} else if e.status != 0 {
parts = append(parts, fmt.Sprintf("status = %d", e.status))
}
if e.err != nil {
parts = append(parts, e.err.Error())
}
if len(parts) == 0 {
return "status = 500"
}
return strings.Join(parts, ": ")
}
func (e ErrorResponse) Unwrap() error {
return e.err
}
// nested response value resolution
func (e ErrorResponse) GetStatus() (int, bool) {
if e.status != 0 {
return e.status, true
}
ce, ok := GetError(e.err)
if ok {
return ce.GetStatus()
}
return 0, false
}
func (e ErrorResponse) GetMsg() (string, bool) {
if e.msg != "" {
return e.msg, true
}
ce, ok := GetError(e.err)
if ok {
return ce.GetMsg()
}
return "", false
}
func (e ErrorResponse) GetHTML() ([]byte, bool) {
if len(e.html) != 0 {
return e.html, true
}
ce, ok := GetError(e.err)
if ok {
return ce.GetHTML()
}
return nil, false
}
func GetError(err error) (e ErrorResponse, ok bool) {
if ok = errors.As(err, &e); ok {
return e, true
}
var ptr *ErrorResponse
if ok = errors.As(err, &ptr); ok {
return *ptr, true
}
return e, ok
}
// error wrapping utilities
func ErrorFromConstant(err error) error {
cerr := err
for cerr != nil {
switch cerr {
case consts.ErrNotFound:
return NotFound()
case consts.ErrConflict:
return Conflict()
}
uerr, ok := cerr.(interface {
Unwrap() error
})
if !ok {
return err
}
cerr = uerr.Unwrap()
}
return err
}
+26
View File
@@ -0,0 +1,26 @@
package response
import (
"github.com/gin-gonic/gin"
)
type (
HandlerFunc = func(c *gin.Context) (Response, error)
Middleware = func(HandlerFunc) HandlerFunc
responseKey struct{}
)
func Handler(f HandlerFunc) gin.HandlerFunc {
return func(c *gin.Context) {
res, err := f(c)
if err != nil {
c.Error(err)
c.Abort()
} else if res != nil {
c.Set(responseKey{}, res)
c.Abort()
}
}
}
+213
View File
@@ -0,0 +1,213 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
headerRes struct {
name string
value string
res Response
}
)
var _ Response = headerRes{}
func header(name, value string) Response {
return headerRes{
name: name,
value: value,
}
}
func HXPushURL(v string) Response {
return header("HX-Push-Url", v)
}
func HXLocation(v string) Response {
return header("HX-Location", v)
}
func HXPushUrl(v string) Response {
return header("HX-Push-Url", v)
}
func HXRedirect(v string) Response {
return header("HX-Redirect", v)
}
func HXRefresh(v string) Response {
return header("HX-Refresh", v)
}
func HXReplaceUrl(v string) Response {
return header("HX-Replace-Url", v)
}
func HXReswap(v string) Response {
return header("HX-Reswap", v)
}
func HXRetarget(v string) Response {
return header("HX-Retarget", v)
}
func HXReselect(v string) Response {
return header("HX-Reselect", v)
}
func HXTrigger(v string) Response {
return header("HX-Trigger", v)
}
func HXTriggerAfterSettle(v string) Response {
return header("HX-Trigger-After-Settle", v)
}
func HXTriggerAfterSwap(v string) Response {
return header("HX-Trigger-After-Swap", v)
}
func (h headerRes) String() string {
if h.res != nil {
return fmt.Sprintf(`{"header": {"name": %q, "value": %q}, "nested": %s}`, h.name, h.value, h.res)
}
return fmt.Sprintf(`{"header": {"name": %q, "value": %q}}`, h.name, h.value)
}
func (h headerRes) wrap(res Response) Response {
h.res = res
return h
}
func (h headerRes) Status(code int) Response {
return Status(code).wrap(h)
}
func (h headerRes) Header(name, value string) Response {
h.name = name
h.value = value
return h
}
func (h headerRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(h)
}
func (h headerRes) HTML(body []byte) Response {
return HTML(body).wrap(h)
}
func (h headerRes) JSON(body any) Response {
return JSON(body).wrap(h)
}
func (h headerRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(h)
}
func (h headerRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(h)
}
func (h headerRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(h)
}
func (h headerRes) HXLocation(v string) Response {
return HXLocation(v).wrap(h)
}
func (h headerRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(h)
}
func (h headerRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(h)
}
func (h headerRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(h)
}
func (h headerRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(h)
}
func (h headerRes) HXReswap(v string) Response {
return HXReswap(v).wrap(h)
}
func (h headerRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(h)
}
func (h headerRes) HXReselect(v string) Response {
return HXReselect(v).wrap(h)
}
func (h headerRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(h)
}
func (h headerRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(h)
}
func (h headerRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(h)
}
func (h headerRes) GetStatus() (int, bool) {
if h.res == nil {
return 0, false
}
return h.res.GetStatus()
}
func (h headerRes) getHeaders() [][2]string {
hdr := [2]string{h.name, h.value}
if h.res == nil {
return [][2]string{hdr}
}
return append(h.res.getHeaders(), hdr)
}
func (h headerRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if h.res == nil {
return 0, "", false
}
return h.res.GetRedirect()
}
func (h headerRes) getBody() (body io.ReadCloser, ok bool, err error) {
if h.res == nil {
return nil, false, nil
}
return h.res.getBody()
}
func (h headerRes) getCookies() []http.Cookie {
if h.res != nil {
return h.res.getCookies()
}
return nil
}
func (h headerRes) getContentType() (contentType string, ok bool) {
if h.res != nil {
return h.res.getContentType()
}
return "", false
}
+149
View File
@@ -0,0 +1,149 @@
package response
import (
"bytes"
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
htmlRes struct {
body []byte
res Response
}
)
var _ Response = htmlRes{}
func HTML(body []byte) Response {
return htmlRes{
body: body,
}
}
func (h htmlRes) String() string {
if h.res != nil {
return fmt.Sprintf(`{"body": %q, "nested": %s}`, string(h.body), h.res)
}
return fmt.Sprintf(`{"body": %q}`, h.body)
}
func (h htmlRes) wrap(res Response) Response {
h.res = res
return h
}
func (h htmlRes) Status(code int) Response {
return Status(code).wrap(h)
}
func (h htmlRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(h)
}
func (h htmlRes) HTML(body []byte) Response {
h.body = body
return h
}
func (h htmlRes) JSON(body any) Response {
return JSON(body).wrap(h)
}
func (h htmlRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(h)
}
func (h htmlRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(h)
}
func (h htmlRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(h)
}
func (h htmlRes) HXLocation(v string) Response {
return HXLocation(v).wrap(h)
}
func (h htmlRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(h)
}
func (h htmlRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(h)
}
func (h htmlRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(h)
}
func (h htmlRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(h)
}
func (h htmlRes) HXReswap(v string) Response {
return HXReswap(v).wrap(h)
}
func (h htmlRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(h)
}
func (h htmlRes) HXReselect(v string) Response {
return HXReselect(v).wrap(h)
}
func (h htmlRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(h)
}
func (h htmlRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(h)
}
func (h htmlRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(h)
}
func (h htmlRes) GetStatus() (int, bool) {
if h.res == nil {
return 0, false
}
return h.res.GetStatus()
}
func (h htmlRes) getHeaders() [][2]string {
if h.res != nil {
return h.res.getHeaders()
}
return nil
}
func (h htmlRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if h.res == nil {
return 0, "", false
}
return h.res.GetRedirect()
}
func (h htmlRes) getBody() (body io.ReadCloser, ok bool, err error) {
return io.NopCloser(bytes.NewBuffer(h.body)), true, nil
}
func (h htmlRes) getCookies() []http.Cookie {
if h.res != nil {
return h.res.getCookies()
}
return nil
}
func (h htmlRes) getContentType() (contentType string, ok bool) {
return "text/html", true
}
+151
View File
@@ -0,0 +1,151 @@
package response
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
jsonRes struct {
body any
res Response
}
)
var _ Response = jsonRes{}
func JSON(body any) Response {
return jsonRes{
body: body,
}
}
func (j jsonRes) String() string {
if j.res != nil {
return fmt.Sprintf(`{"body": %q, "nested": %s}`, j.body, j.res)
}
return fmt.Sprintf(`{"body": %q}`, j.body)
}
func (j jsonRes) wrap(res Response) Response {
j.res = res
return j
}
func (j jsonRes) Status(code int) Response {
return Status(code).wrap(j)
}
func (j jsonRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(j)
}
func (j jsonRes) HTML(body []byte) Response {
return HTML(body).wrap(j)
}
func (j jsonRes) JSON(body any) Response {
j.body = body
return j
}
func (j jsonRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(j)
}
func (j jsonRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(j)
}
func (j jsonRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(j)
}
func (j jsonRes) HXLocation(v string) Response {
return HXLocation(v).wrap(j)
}
func (j jsonRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(j)
}
func (j jsonRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(j)
}
func (j jsonRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(j)
}
func (j jsonRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(j)
}
func (j jsonRes) HXReswap(v string) Response {
return HXReswap(v).wrap(j)
}
func (j jsonRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(j)
}
func (j jsonRes) HXReselect(v string) Response {
return HXReselect(v).wrap(j)
}
func (j jsonRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(j)
}
func (j jsonRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(j)
}
func (j jsonRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(j)
}
func (j jsonRes) GetStatus() (int, bool) {
if j.res == nil {
return 0, false
}
return j.res.GetStatus()
}
func (j jsonRes) getHeaders() [][2]string {
if j.res != nil {
return j.res.getHeaders()
}
return nil
}
func (j jsonRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if j.res == nil {
return 0, "", false
}
return j.res.GetRedirect()
}
func (j jsonRes) getBody() (body io.ReadCloser, ok bool, err error) {
buf := new(bytes.Buffer)
return io.NopCloser(buf), true, json.NewEncoder(buf).Encode(j.body)
}
func (j jsonRes) getCookies() []http.Cookie {
if j.res != nil {
return j.res.getCookies()
}
return nil
}
func (j jsonRes) getContentType() (contentType string, ok bool) {
return "application/json", true
}
+192
View File
@@ -0,0 +1,192 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
redirectRes struct {
code redirect.Code
to string
res Response
}
)
var _ Response = redirectRes{}
// convenience constructors
func MovedPermanently(to string) Response {
return redirectRes{
code: redirect.MovedPermanently,
to: to,
}
}
func Found(to string) Response {
return redirectRes{
code: redirect.Found,
to: to,
}
}
func SeeOther(to string) Response {
return redirectRes{
code: redirect.SeeOther,
to: to,
}
}
func TemporaryRedirect(to string) Response {
return redirectRes{
code: redirect.TemporaryRedirect,
to: to,
}
}
func PermanentRedirect(to string) Response {
return redirectRes{
code: redirect.PermanentRedirect,
to: to,
}
}
func Redirect(code redirect.Code, to string) Response {
return redirectRes{
code: code,
to: to,
}
}
func (r redirectRes) String() string {
if r.res != nil {
return fmt.Sprintf(`{"redirect": {"code": %d, "to": %q}, "nested": %s}`, r.code, r.to, r.res)
}
return fmt.Sprintf(`{"redirect": {"code": %d, "to": %q}}`, r.code, r.to)
}
func (r redirectRes) wrap(res Response) Response {
r.res = res
return r
}
func (r redirectRes) Status(code int) Response {
return Status(code).wrap(r)
}
func (r redirectRes) Redirect(code redirect.Code, to string) Response {
r.code = code
r.to = to
return r
}
func (r redirectRes) HTML(body []byte) Response {
return HTML(body).wrap(r)
}
func (r redirectRes) JSON(body any) Response {
return JSON(body).wrap(r)
}
func (r redirectRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(r)
}
func (r redirectRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(r)
}
func (r redirectRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(r)
}
func (r redirectRes) HXLocation(v string) Response {
return HXLocation(v).wrap(r)
}
func (r redirectRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(r)
}
func (r redirectRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(r)
}
func (r redirectRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(r)
}
func (r redirectRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(r)
}
func (r redirectRes) HXReswap(v string) Response {
return HXReswap(v).wrap(r)
}
func (r redirectRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(r)
}
func (r redirectRes) HXReselect(v string) Response {
return HXReselect(v).wrap(r)
}
func (r redirectRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(r)
}
func (r redirectRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(r)
}
func (r redirectRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(r)
}
func (r redirectRes) GetStatus() (int, bool) {
if r.res == nil {
return 0, false
}
return r.res.GetStatus()
}
func (r redirectRes) getHeaders() [][2]string {
if r.res != nil {
return r.res.getHeaders()
}
return nil
}
func (r redirectRes) GetRedirect() (code redirect.Code, to string, ok bool) {
return r.code, r.to, true
}
func (r redirectRes) getBody() (body io.ReadCloser, ok bool, err error) {
if r.res == nil {
return nil, false, nil
}
return r.res.getBody()
}
func (r redirectRes) getCookies() []http.Cookie {
if r.res != nil {
return r.res.getCookies()
}
return nil
}
func (r redirectRes) getContentType() (contentType string, ok bool) {
if r.res != nil {
return r.res.getContentType()
}
return "", false
}
+40
View File
@@ -0,0 +1,40 @@
package response
import (
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
Response interface {
Status(int) Response
HXPushURL(string) Response
HXLocation(string) Response
HXPushUrl(string) Response
HXRedirect(string) Response
HXRefresh(string) Response
HXReplaceUrl(string) Response
HXReswap(string) Response
HXRetarget(string) Response
HXReselect(string) Response
HXTrigger(string) Response
HXTriggerAfterSettle(string) Response
HXTriggerAfterSwap(string) Response
Redirect(code redirect.Code, to string) Response
Body(io.ReadCloser) Response
HTML([]byte) Response
JSON(any) Response
Cookie(http.Cookie) Response
GetStatus() (code int, ok bool)
getBody() (body io.ReadCloser, ok bool, err error)
GetRedirect() (code redirect.Code, to string, ok bool)
getCookies() []http.Cookie
getContentType() (contentType string, ok bool)
getHeaders() [][2]string
wrap(Response) Response
}
)
+168
View File
@@ -0,0 +1,168 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/server/redirect"
)
type (
statusRes struct {
code int
res Response
}
)
var _ Response = statusRes{}
func Status(code int) Response {
return statusRes{
code: code,
}
}
func StatusOK() Response {
return Status(http.StatusOK)
}
func StatusCreated() Response {
return Status(http.StatusCreated)
}
func StatusAccepted() Response {
return Status(http.StatusAccepted)
}
func StatusNoContent() Response {
return Status(http.StatusNoContent)
}
func (s statusRes) String() string {
if s.res != nil {
return fmt.Sprintf(`{"status": %d, "nested": %s}`, s.code, s.res)
}
return fmt.Sprintf(`{"status": %d}`, s.code)
}
func (s statusRes) wrap(res Response) Response {
s.res = res
return s
}
func (s statusRes) Status(code int) Response {
s.code = code
return s
}
func (s statusRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(s)
}
func (s statusRes) HTML(body []byte) Response {
return HTML(body).wrap(s)
}
func (s statusRes) JSON(body any) Response {
return JSON(body).wrap(s)
}
func (s statusRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(s)
}
func (s statusRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(s)
}
func (s statusRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(s)
}
func (s statusRes) HXLocation(v string) Response {
return HXLocation(v).wrap(s)
}
func (s statusRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(s)
}
func (s statusRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(s)
}
func (s statusRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(s)
}
func (s statusRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(s)
}
func (s statusRes) HXReswap(v string) Response {
return HXReswap(v).wrap(s)
}
func (s statusRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(s)
}
func (s statusRes) HXReselect(v string) Response {
return HXReselect(v).wrap(s)
}
func (s statusRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(s)
}
func (s statusRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(s)
}
func (s statusRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(s)
}
func (s statusRes) GetStatus() (int, bool) {
return s.code, true
}
func (s statusRes) getHeaders() [][2]string {
if s.res != nil {
return s.res.getHeaders()
}
return nil
}
func (s statusRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if s.res == nil {
return 0, "", false
}
return s.res.GetRedirect()
}
func (s statusRes) getBody() (body io.ReadCloser, ok bool, err error) {
if s.res == nil {
return nil, false, nil
}
return s.res.getBody()
}
func (s statusRes) getCookies() []http.Cookie {
if s.res != nil {
return s.res.getCookies()
}
return nil
}
func (s statusRes) getContentType() (contentType string, ok bool) {
if s.res != nil {
return s.res.getContentType()
}
return "", false
}
+168
View File
@@ -0,0 +1,168 @@
package response
import (
"errors"
"fmt"
"io"
"net/http"
"github.com/gin-gonic/gin"
"ruben/inventory2/consts"
)
func HandleResponses(c *gin.Context) {
c.Next()
if len(c.Errors) > 0 || c.Writer.Written() {
return
}
v, ok := c.Get(responseKey{})
if !ok {
return
}
res, ok := v.(Response)
if !ok {
return
}
writeResponse(c, res)
}
func HandleErrors(c *gin.Context) {
c.Next()
if len(c.Errors) == 0 || c.Writer.Written() {
return
}
var (
err ErrorResponse
ok bool
)
for _, e := range c.Errors {
if err, ok = GetError(e); ok {
break
}
}
if !ok {
var (
err error
status int
statusFound bool
)
for _, e := range c.Errors {
err = errors.Join(err, e)
if !statusFound {
status, statusFound = mapErrorConstantsToStatus(e)
}
}
if !statusFound {
status = http.StatusInternalServerError
}
c.String(status, err.Error())
return
}
status, ok := err.GetStatus()
if !ok {
status = http.StatusInternalServerError
}
if h, ok := err.GetHTML(); ok {
c.Status(status)
c.Header("Content-Type", "text/html")
c.Writer.Write(h)
return
}
msg, ok := err.GetMsg()
if !ok {
msg = err.Error()
}
c.String(status, msg)
}
func writeResponse(c *gin.Context, res Response) {
w := c.Writer
// w.Header() must be set before ResponseWriter.WriteHeader is called
// or redirect is attempted
hdrs := w.Header()
for _, hdr := range res.getHeaders() {
hdrs.Add(hdr[0], hdr[1])
}
for _, ck := range res.getCookies() {
hdrs.Add("Set-Cookie", ck.String())
}
if ct, ok := res.getContentType(); ok {
hdrs.Set("Content-Type", ct)
}
if code, to, ok := res.GetRedirect(); ok {
c.Redirect(code.Int(), to)
c.Abort()
return
}
// the body is written after the status header, but it's read here
// first, because if an error is incurred, an error status header will
// need to be written.
body, bodySet, err := res.getBody()
if err != nil {
http.Error(
w,
fmt.Sprintf("Failed to construct response body: %v", err.Error()),
http.StatusInternalServerError,
)
return
}
if status, ok := res.GetStatus(); ok {
w.WriteHeader(status)
}
if bodySet {
// will automatically set the status header,
// if w.WriteHeader wasn't already called
io.Copy(w, body)
}
}
func GetStatusFromError(err error) int {
status := http.StatusInternalServerError
if e, ok := GetError(err); ok {
if status, ok = e.GetStatus(); !ok {
status = http.StatusInternalServerError
}
}
return status
}
func mapErrorConstantsToStatus(err error) (int, bool) {
for {
switch err {
case consts.ErrBadRequest:
return http.StatusBadRequest, true
case consts.ErrNotFound:
return http.StatusNotFound, true
case consts.ErrConflict:
return http.StatusConflict, true
default:
werr, ok := err.(interface {
Unwrap() error
})
if !ok {
return 0, false
}
err = werr.Unwrap()
}
}
}