You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ims/util/rsp/response.go

158 lines
2.9 KiB

package rsp
import (
"errors"
"fmt"
"net/http"
"runtime"
"zestack.dev/slim"
"zestack.dev/v"
)
type response struct {
status int
headers map[string]string
cookies []*http.Cookie
err error
message string
data any
}
type Option func(o *response)
func StatusCode(status int) Option {
return func(o *response) {
o.status = status
}
}
func Header(key, value string) Option {
return func(o *response) {
if o.headers == nil {
o.headers = make(map[string]string)
}
o.headers[key] = value
}
}
func Cookie(cookie *http.Cookie) Option {
return func(o *response) {
if o.cookies != nil {
for i, h := range o.cookies {
if h.Name == cookie.Name {
o.cookies[i] = cookie
return
}
}
}
o.cookies = append(o.cookies, cookie)
}
}
func Message(msg string) Option {
return func(o *response) {
o.message = msg
}
}
func Data(data any) Option {
return func(o *response) {
o.data = data
}
}
func (r *response) result(c slim.Context) (m map[string]any, status int) {
status = r.status
m = map[string]any{
"code": nil,
"success": false,
"message": r.message,
}
if r.err == nil {
m["code"] = ErrOK.code
m["message"] = ErrOK.text
m["success"] = true
if r.data != nil {
m["data"] = r.data
}
if m["message"] == "" {
m["message"] = http.StatusText(status)
}
return
}
var verr *v.Errors
var rerr *Error
var err error
if errors.As(r.err, &verr) {
err = nil
problems := make(Problems)
for _, e := range verr.All() {
message := e.String()
if message == "" {
message = e.Error()
}
problems.Add(&Problem{
Label: e.Field(),
Code: e.Code(),
Message: message,
Problems: nil,
})
}
m["code"] = ErrBadParams.code
m["message"] = ErrBadParams.text
m["problems"] = problems
if status < 400 {
status = ErrBadParams.status
}
} else if errors.As(r.err, &rerr) {
m["code"] = rerr.code
m["success"] = errors.Is(rerr, ErrOK)
m["message"] = rerr.text
if data := rerr.Data(); data != nil {
m["data"] = data
}
if status < 400 {
status = rerr.status
}
err = rerr.internal
} else {
var he *slim.HTTPError
if errors.As(r.err, &he) {
status = he.StatusCode
err = he.Internal
m["message"] = he.Message
} else {
m["message"] = r.err.Error()
}
m["code"] = ErrInternal.Code()
if status < 400 {
status = ErrInternal.status
}
}
if c.Slim().Debug && m["success"] != true {
if err != nil {
m["error"] = err.Error()
}
m["stack"] = relevantCaller()
}
if m["message"] == "" {
m["message"] = http.StatusText(status)
}
return
}
func relevantCaller() []string {
pc := make([]uintptr, 16)
n := runtime.Callers(4, pc)
frames := runtime.CallersFrames(pc[:n])
var traces []string
for {
frame, more := frames.Next()
traces = append(traces, fmt.Sprintf("%s:%s:%d", frame.File, frame.Func.Name(), frame.Line))
if !more {
return traces
}
}
}