package ginext import ( "fmt" "github.com/gin-gonic/gin" "gogs.mikescher.com/BlackForestBytes/goext/exerr" json "gogs.mikescher.com/BlackForestBytes/goext/gojson" ) type headerval struct { Key string Val string } type HTTPResponse interface { Write(g *gin.Context) WithHeader(k string, v string) HTTPResponse } type jsonHTTPResponse struct { statusCode int data any headers []headerval } func (j jsonHTTPResponse) Write(g *gin.Context) { for _, v := range j.headers { g.Header(v.Key, v.Val) } g.Render(j.statusCode, json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true}) } func (j jsonHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type emptyHTTPResponse struct { statusCode int headers []headerval } func (j emptyHTTPResponse) Write(g *gin.Context) { for _, v := range j.headers { g.Header(v.Key, v.Val) } g.Status(j.statusCode) } func (j emptyHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type textHTTPResponse struct { statusCode int data string headers []headerval } func (j textHTTPResponse) Write(g *gin.Context) { for _, v := range j.headers { g.Header(v.Key, v.Val) } g.String(j.statusCode, "%s", j.data) } func (j textHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type dataHTTPResponse struct { statusCode int data []byte contentType string headers []headerval } func (j dataHTTPResponse) Write(g *gin.Context) { for _, v := range j.headers { g.Header(v.Key, v.Val) } g.Data(j.statusCode, j.contentType, j.data) } func (j dataHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type fileHTTPResponse struct { mimetype string filepath string filename *string headers []headerval } func (j fileHTTPResponse) Write(g *gin.Context) { g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later... if j.filename != nil { g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename)) } for _, v := range j.headers { g.Header(v.Key, v.Val) } g.File(j.filepath) } func (j fileHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type downloadDataHTTPResponse struct { statusCode int mimetype string data []byte filename *string headers []headerval } func (j downloadDataHTTPResponse) Write(g *gin.Context) { g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later... if j.filename != nil { g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename)) } for _, v := range j.headers { g.Header(v.Key, v.Val) } g.Data(j.statusCode, j.mimetype, j.data) } func (j downloadDataHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type redirectHTTPResponse struct { statusCode int url string headers []headerval } func (j redirectHTTPResponse) Write(g *gin.Context) { g.Redirect(j.statusCode, j.url) } func (j redirectHTTPResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } type jsonAPIErrResponse struct { err *exerr.ExErr headers []headerval } func (j jsonAPIErrResponse) Write(g *gin.Context) { j.err.Output(g) } func (j jsonAPIErrResponse) WithHeader(k string, v string) HTTPResponse { j.headers = append(j.headers, headerval{k, v}) return j } func Status(sc int) HTTPResponse { return &emptyHTTPResponse{statusCode: sc} } func JSON(sc int, data any) HTTPResponse { return &jsonHTTPResponse{statusCode: sc, data: data} } func Data(sc int, contentType string, data []byte) HTTPResponse { return &dataHTTPResponse{statusCode: sc, contentType: contentType, data: data} } func Text(sc int, data string) HTTPResponse { return &textHTTPResponse{statusCode: sc, data: data} } func File(mimetype string, filepath string) HTTPResponse { return &fileHTTPResponse{mimetype: mimetype, filepath: filepath} } func Download(mimetype string, filepath string, filename string) HTTPResponse { return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename} } func DownloadData(status int, mimetype string, filename string, data []byte) HTTPResponse { return &downloadDataHTTPResponse{statusCode: status, mimetype: mimetype, data: data, filename: &filename} } func Redirect(sc int, newURL string) HTTPResponse { return &redirectHTTPResponse{statusCode: sc, url: newURL} } func Error(e error) HTTPResponse { return &jsonAPIErrResponse{ err: exerr.FromError(e), } } func ErrWrap(e error, errorType exerr.ErrorType, msg string) HTTPResponse { return &jsonAPIErrResponse{ err: exerr.FromError(exerr.Wrap(e, msg).WithType(errorType).Build()), } } func NotImplemented() HTTPResponse { return Error(exerr.New(exerr.TypeNotImplemented, "").Build()) }