package response import ( "encoding/json" "net/http" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" ) // Result is the transport-independent input to an Encoder. Err is available for // application error mapping; default encoders never expose it or its causes. type Result struct { Status int Code, Message, RequestID string Data any Err error BusinessCode any Failed bool } // IsError preserves failure semantics independently of the HTTP status. func (r Result) IsError() bool { return r.Failed || r.Err != nil || r.Status >= 400 } // Payload is a complete response. Encoders must be safe for concurrent use. // Status and headers are intentionally part of the contract: callback protocols // may acknowledge an error using a different HTTP status or media type. type Payload struct { Status int Header http.Header Body []byte } type Encoder interface{ Encode(Result) (Payload, error) } type EncoderFunc func(Result) (Payload, error) func (f EncoderFunc) Encode(r Result) (Payload, error) { return f(r) } func JSON(status int, value any) (Payload, error) { data, err := json.Marshal(value) if err != nil { return Payload{}, err } return Payload{Status: status, Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}}, Body: append(data, '\n')}, nil } func Text(status int, value string) Payload { return Payload{Status: status, Header: http.Header{"Content-Type": {"text/plain; charset=utf-8"}}, Body: []byte(value)} } // Default is immutable; applications can use different encoders in one process. var defaultEncoder = MustNew(Config{}) func Default() Encoder { return defaultEncoder } // RawJSON leaves successful data unwrapped. Errors use the selected error // policy; callback protocols with custom acknowledgments should use EncoderFunc. type rawJSONEncoder struct{ errors Encoder } func (e *rawJSONEncoder) ErrorRegistry() *apierrors.Registry { if p, ok := e.errors.(ErrorRegistryProvider); ok { return p.ErrorRegistry() } return nil } func (e *rawJSONEncoder) Encode(r Result) (Payload, error) { if r.IsError() { return e.errors.Encode(r) } return JSON(r.Status, r.Data) } func RawJSON(errors Encoder) Encoder { if errors == nil { errors = Default() } return &rawJSONEncoder{errors: errors} } func (e *rawJSONEncoder) ValidateErrors(catalog apierrors.Catalog) error { if checker, ok := e.errors.(interface{ ValidateErrors(apierrors.Catalog) error }); ok { return checker.ValidateErrors(catalog) } return nil }