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.
158 lines
2.9 KiB
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
|
|
}
|
|
}
|
|
}
|
|
|