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