github.com/jackc/pgx/v5@v5.5.5/pgtype/circle.go (about) 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 // Scan implements the database/sql Scanner interface. 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 // Value implements the database/sql/driver Valuer interface. 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 }