1 package pgtype
2
3 import (
4 "database/sql/driver"
5 "encoding/binary"
6 "fmt"
7 "math"
8 "strconv"
9 "strings"
10
11 "github.com/jackc/pgx/v5/internal/pgio"
12 )
13
14 type CircleScanner interface {
15 ScanCircle(v Circle) error
16 }
17
18 type CircleValuer interface {
19 CircleValue() (Circle, error)
20 }
21
22 type Circle struct {
23 P Vec2
24 R float64
25 Valid bool
26 }
27
28 func (c *Circle) ScanCircle(v Circle) error {
29 *c = v
30 return nil
31 }
32
33 func (c Circle) CircleValue() (Circle, error) {
34 return c, nil
35 }
36
37
38 func (dst *Circle) Scan(src any) error {
39 if src == nil {
40 *dst = Circle{}
41 return nil
42 }
43
44 switch src := src.(type) {
45 case string:
46 return scanPlanTextAnyToCircleScanner{}.Scan([]byte(src), dst)
47 }
48
49 return fmt.Errorf("cannot scan %T", src)
50 }
51
52
53 func (src Circle) Value() (driver.Value, error) {
54 if !src.Valid {
55 return nil, nil
56 }
57
58 buf, err := CircleCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)
59 if err != nil {
60 return nil, err
61 }
62 return string(buf), err
63 }
64
65 type CircleCodec struct{}
66
67 func (CircleCodec) FormatSupported(format int16) bool {
68 return format == TextFormatCode || format == BinaryFormatCode
69 }
70
71 func (CircleCodec) PreferredFormat() int16 {
72 return BinaryFormatCode
73 }
74
75 func (CircleCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
76 if _, ok := value.(CircleValuer); !ok {
77 return nil
78 }
79
80 switch format {
81 case BinaryFormatCode:
82 return encodePlanCircleCodecBinary{}
83 case TextFormatCode:
84 return encodePlanCircleCodecText{}
85 }
86
87 return nil
88 }
89
90 type encodePlanCircleCodecBinary struct{}
91
92 func (encodePlanCircleCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
93 circle, err := value.(CircleValuer).CircleValue()
94 if err != nil {
95 return nil, err
96 }
97
98 if !circle.Valid {
99 return nil, nil
100 }
101
102 buf = pgio.AppendUint64(buf, math.Float64bits(circle.P.X))
103 buf = pgio.AppendUint64(buf, math.Float64bits(circle.P.Y))
104 buf = pgio.AppendUint64(buf, math.Float64bits(circle.R))
105 return buf, nil
106 }
107
108 type encodePlanCircleCodecText struct{}
109
110 func (encodePlanCircleCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
111 circle, err := value.(CircleValuer).CircleValue()
112 if err != nil {
113 return nil, err
114 }
115
116 if !circle.Valid {
117 return nil, nil
118 }
119
120 buf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`,
121 strconv.FormatFloat(circle.P.X, 'f', -1, 64),
122 strconv.FormatFloat(circle.P.Y, 'f', -1, 64),
123 strconv.FormatFloat(circle.R, 'f', -1, 64),
124 )...)
125 return buf, nil
126 }
127
128 func (CircleCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
129 switch format {
130 case BinaryFormatCode:
131 switch target.(type) {
132 case CircleScanner:
133 return scanPlanBinaryCircleToCircleScanner{}
134 }
135 case TextFormatCode:
136 switch target.(type) {
137 case CircleScanner:
138 return scanPlanTextAnyToCircleScanner{}
139 }
140 }
141
142 return nil
143 }
144
145 func (c CircleCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
146 return codecDecodeToTextFormat(c, m, oid, format, src)
147 }
148
149 func (c CircleCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
150 if src == nil {
151 return nil, nil
152 }
153
154 var circle Circle
155 err := codecScan(c, m, oid, format, src, &circle)
156 if err != nil {
157 return nil, err
158 }
159 return circle, nil
160 }
161
162 type scanPlanBinaryCircleToCircleScanner struct{}
163
164 func (scanPlanBinaryCircleToCircleScanner) Scan(src []byte, dst any) error {
165 scanner := (dst).(CircleScanner)
166
167 if src == nil {
168 return scanner.ScanCircle(Circle{})
169 }
170
171 if len(src) != 24 {
172 return fmt.Errorf("invalid length for Circle: %v", len(src))
173 }
174
175 x := binary.BigEndian.Uint64(src)
176 y := binary.BigEndian.Uint64(src[8:])
177 r := binary.BigEndian.Uint64(src[16:])
178
179 return scanner.ScanCircle(Circle{
180 P: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
181 R: math.Float64frombits(r),
182 Valid: true,
183 })
184 }
185
186 type scanPlanTextAnyToCircleScanner struct{}
187
188 func (scanPlanTextAnyToCircleScanner) Scan(src []byte, dst any) error {
189 scanner := (dst).(CircleScanner)
190
191 if src == nil {
192 return scanner.ScanCircle(Circle{})
193 }
194
195 if len(src) < 9 {
196 return fmt.Errorf("invalid length for Circle: %v", len(src))
197 }
198
199 str := string(src[2:])
200 end := strings.IndexByte(str, ',')
201 x, err := strconv.ParseFloat(str[:end], 64)
202 if err != nil {
203 return err
204 }
205
206 str = str[end+1:]
207 end = strings.IndexByte(str, ')')
208
209 y, err := strconv.ParseFloat(str[:end], 64)
210 if err != nil {
211 return err
212 }
213
214 str = str[end+2 : len(str)-1]
215
216 r, err := strconv.ParseFloat(str, 64)
217 if err != nil {
218 return err
219 }
220
221 return scanner.ScanCircle(Circle{P: Vec2{x, y}, R: r, Valid: true})
222 }
223
View as plain text