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/pgio"
12 )
13
14 type Polygon struct {
15 P []Vec2
16 Status Status
17 }
18
19
20
21
22
23
24
25 func (dst *Polygon) Set(src interface{}) error {
26 if src == nil {
27 dst.Status = Null
28 return nil
29 }
30 err := fmt.Errorf("cannot convert %v to Polygon", src)
31 var p *Polygon
32 switch value := src.(type) {
33 case string:
34 p, err = stringToPolygon(value)
35 case []Vec2:
36 p = &Polygon{Status: Present, P: value}
37 err = nil
38 case []float64:
39 p, err = float64ToPolygon(value)
40 default:
41 return err
42 }
43 if err != nil {
44 return err
45 }
46 *dst = *p
47 return nil
48 }
49
50 func stringToPolygon(src string) (*Polygon, error) {
51 p := &Polygon{}
52 err := p.DecodeText(nil, []byte(src))
53 return p, err
54 }
55
56 func float64ToPolygon(src []float64) (*Polygon, error) {
57 p := &Polygon{Status: Null}
58 if len(src) == 0 {
59 return p, nil
60 }
61 if len(src)%2 != 0 {
62 p.Status = Undefined
63 return p, fmt.Errorf("invalid length for polygon: %v", len(src))
64 }
65 p.Status = Present
66 p.P = make([]Vec2, 0)
67 for i := 0; i < len(src); i += 2 {
68 p.P = append(p.P, Vec2{X: src[i], Y: src[i+1]})
69 }
70 return p, nil
71 }
72
73 func (dst Polygon) Get() interface{} {
74 switch dst.Status {
75 case Present:
76 return dst
77 case Null:
78 return nil
79 default:
80 return dst.Status
81 }
82 }
83
84 func (src *Polygon) AssignTo(dst interface{}) error {
85 return fmt.Errorf("cannot assign %v to %T", src, dst)
86 }
87
88 func (dst *Polygon) DecodeText(ci *ConnInfo, src []byte) error {
89 if src == nil {
90 *dst = Polygon{Status: Null}
91 return nil
92 }
93
94 if len(src) < 7 {
95 return fmt.Errorf("invalid length for Polygon: %v", len(src))
96 }
97
98 points := make([]Vec2, 0)
99
100 str := string(src[2:])
101
102 for {
103 end := strings.IndexByte(str, ',')
104 x, err := strconv.ParseFloat(str[:end], 64)
105 if err != nil {
106 return err
107 }
108
109 str = str[end+1:]
110 end = strings.IndexByte(str, ')')
111
112 y, err := strconv.ParseFloat(str[:end], 64)
113 if err != nil {
114 return err
115 }
116
117 points = append(points, Vec2{x, y})
118
119 if end+3 < len(str) {
120 str = str[end+3:]
121 } else {
122 break
123 }
124 }
125
126 *dst = Polygon{P: points, Status: Present}
127 return nil
128 }
129
130 func (dst *Polygon) DecodeBinary(ci *ConnInfo, src []byte) error {
131 if src == nil {
132 *dst = Polygon{Status: Null}
133 return nil
134 }
135
136 if len(src) < 5 {
137 return fmt.Errorf("invalid length for Polygon: %v", len(src))
138 }
139
140 pointCount := int(binary.BigEndian.Uint32(src))
141 rp := 4
142
143 if 4+pointCount*16 != len(src) {
144 return fmt.Errorf("invalid length for Polygon with %d points: %v", pointCount, len(src))
145 }
146
147 points := make([]Vec2, pointCount)
148 for i := 0; i < len(points); i++ {
149 x := binary.BigEndian.Uint64(src[rp:])
150 rp += 8
151 y := binary.BigEndian.Uint64(src[rp:])
152 rp += 8
153 points[i] = Vec2{math.Float64frombits(x), math.Float64frombits(y)}
154 }
155
156 *dst = Polygon{
157 P: points,
158 Status: Present,
159 }
160 return nil
161 }
162
163 func (src Polygon) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
164 switch src.Status {
165 case Null:
166 return nil, nil
167 case Undefined:
168 return nil, errUndefined
169 }
170
171 buf = append(buf, '(')
172
173 for i, p := range src.P {
174 if i > 0 {
175 buf = append(buf, ',')
176 }
177 buf = append(buf, fmt.Sprintf(`(%s,%s)`,
178 strconv.FormatFloat(p.X, 'f', -1, 64),
179 strconv.FormatFloat(p.Y, 'f', -1, 64),
180 )...)
181 }
182
183 return append(buf, ')'), nil
184 }
185
186 func (src Polygon) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
187 switch src.Status {
188 case Null:
189 return nil, nil
190 case Undefined:
191 return nil, errUndefined
192 }
193
194 buf = pgio.AppendInt32(buf, int32(len(src.P)))
195
196 for _, p := range src.P {
197 buf = pgio.AppendUint64(buf, math.Float64bits(p.X))
198 buf = pgio.AppendUint64(buf, math.Float64bits(p.Y))
199 }
200
201 return buf, nil
202 }
203
204
205 func (dst *Polygon) Scan(src interface{}) error {
206 if src == nil {
207 *dst = Polygon{Status: Null}
208 return nil
209 }
210
211 switch src := src.(type) {
212 case string:
213 return dst.DecodeText(nil, []byte(src))
214 case []byte:
215 srcCopy := make([]byte, len(src))
216 copy(srcCopy, src)
217 return dst.DecodeText(nil, srcCopy)
218 }
219
220 return fmt.Errorf("cannot scan %T", src)
221 }
222
223
224 func (src Polygon) Value() (driver.Value, error) {
225 return EncodeValueText(src)
226 }
227
View as plain text