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 } } }