package response import ( "encoding/json" "net/http" ) // 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 } // 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. func RawJSON(errors Encoder) Encoder { if errors == nil { errors = Default() } return EncoderFunc(func(r Result) (Payload, error) { if r.Err != nil || r.Status >= 400 { return errors.Encode(r) } return JSON(r.Status, r.Data) }) }