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