1package pgproto3
2
3import (
4	"encoding/binary"
5	"encoding/hex"
6	"encoding/json"
7
8	"github.com/jackc/pgio"
9)
10
11type FunctionCallResponse struct {
12	Result []byte
13}
14
15// Backend identifies this message as sendable by the PostgreSQL backend.
16func (*FunctionCallResponse) Backend() {}
17
18// Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
19// type identifier and 4 byte message length.
20func (dst *FunctionCallResponse) Decode(src []byte) error {
21	if len(src) < 4 {
22		return &invalidMessageFormatErr{messageType: "FunctionCallResponse"}
23	}
24	rp := 0
25	resultSize := int(binary.BigEndian.Uint32(src[rp:]))
26	rp += 4
27
28	if resultSize == -1 {
29		dst.Result = nil
30		return nil
31	}
32
33	if len(src[rp:]) != resultSize {
34		return &invalidMessageFormatErr{messageType: "FunctionCallResponse"}
35	}
36
37	dst.Result = src[rp:]
38	return nil
39}
40
41// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
42func (src *FunctionCallResponse) Encode(dst []byte) []byte {
43	dst = append(dst, 'V')
44	sp := len(dst)
45	dst = pgio.AppendInt32(dst, -1)
46
47	if src.Result == nil {
48		dst = pgio.AppendInt32(dst, -1)
49	} else {
50		dst = pgio.AppendInt32(dst, int32(len(src.Result)))
51		dst = append(dst, src.Result...)
52	}
53
54	pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
55
56	return dst
57}
58
59// MarshalJSON implements encoding/json.Marshaler.
60func (src FunctionCallResponse) MarshalJSON() ([]byte, error) {
61	var formattedValue map[string]string
62	var hasNonPrintable bool
63	for _, b := range src.Result {
64		if b < 32 {
65			hasNonPrintable = true
66			break
67		}
68	}
69
70	if hasNonPrintable {
71		formattedValue = map[string]string{"binary": hex.EncodeToString(src.Result)}
72	} else {
73		formattedValue = map[string]string{"text": string(src.Result)}
74	}
75
76	return json.Marshal(struct {
77		Type   string
78		Result map[string]string
79	}{
80		Type:   "FunctionCallResponse",
81		Result: formattedValue,
82	})
83}
84
85// UnmarshalJSON implements encoding/json.Unmarshaler.
86func (dst *FunctionCallResponse) UnmarshalJSON(data []byte) error {
87	// Ignore null, like in the main JSON package.
88	if string(data) == "null" {
89		return nil
90	}
91
92	var msg struct {
93		Result map[string]string
94	}
95	err := json.Unmarshal(data, &msg)
96	if err != nil {
97		return err
98	}
99	dst.Result, err = getValueFromJSON(msg.Result)
100	return err
101}
102