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