mirror of https://github.com/jackc/pgx.git
223 lines
4.6 KiB
Go
223 lines
4.6 KiB
Go
package pgtype
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/jackc/pgx/v5/internal/pgio"
|
|
)
|
|
|
|
type CircleScanner interface {
|
|
ScanCircle(v Circle) error
|
|
}
|
|
|
|
type CircleValuer interface {
|
|
CircleValue() (Circle, error)
|
|
}
|
|
|
|
type Circle struct {
|
|
P Vec2
|
|
R float64
|
|
Valid bool
|
|
}
|
|
|
|
func (c *Circle) ScanCircle(v Circle) error {
|
|
*c = v
|
|
return nil
|
|
}
|
|
|
|
func (c Circle) CircleValue() (Circle, error) {
|
|
return c, nil
|
|
}
|
|
|
|
// Scan implements the database/sql Scanner interface.
|
|
func (dst *Circle) Scan(src any) error {
|
|
if src == nil {
|
|
*dst = Circle{}
|
|
return nil
|
|
}
|
|
|
|
switch src := src.(type) {
|
|
case string:
|
|
return scanPlanTextAnyToCircleScanner{}.Scan([]byte(src), dst)
|
|
}
|
|
|
|
return fmt.Errorf("cannot scan %T", src)
|
|
}
|
|
|
|
// Value implements the database/sql/driver Valuer interface.
|
|
func (src Circle) Value() (driver.Value, error) {
|
|
if !src.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
buf, err := CircleCodec{}.PlanEncode(nil, 0, TextFormatCode, src).Encode(src, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return string(buf), err
|
|
}
|
|
|
|
type CircleCodec struct{}
|
|
|
|
func (CircleCodec) FormatSupported(format int16) bool {
|
|
return format == TextFormatCode || format == BinaryFormatCode
|
|
}
|
|
|
|
func (CircleCodec) PreferredFormat() int16 {
|
|
return BinaryFormatCode
|
|
}
|
|
|
|
func (CircleCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
|
if _, ok := value.(CircleValuer); !ok {
|
|
return nil
|
|
}
|
|
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
return encodePlanCircleCodecBinary{}
|
|
case TextFormatCode:
|
|
return encodePlanCircleCodecText{}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type encodePlanCircleCodecBinary struct{}
|
|
|
|
func (encodePlanCircleCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
circle, err := value.(CircleValuer).CircleValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !circle.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
buf = pgio.AppendUint64(buf, math.Float64bits(circle.P.X))
|
|
buf = pgio.AppendUint64(buf, math.Float64bits(circle.P.Y))
|
|
buf = pgio.AppendUint64(buf, math.Float64bits(circle.R))
|
|
return buf, nil
|
|
}
|
|
|
|
type encodePlanCircleCodecText struct{}
|
|
|
|
func (encodePlanCircleCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
circle, err := value.(CircleValuer).CircleValue()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !circle.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
buf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`,
|
|
strconv.FormatFloat(circle.P.X, 'f', -1, 64),
|
|
strconv.FormatFloat(circle.P.Y, 'f', -1, 64),
|
|
strconv.FormatFloat(circle.R, 'f', -1, 64),
|
|
)...)
|
|
return buf, nil
|
|
}
|
|
|
|
func (CircleCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
switch target.(type) {
|
|
case CircleScanner:
|
|
return scanPlanBinaryCircleToCircleScanner{}
|
|
}
|
|
case TextFormatCode:
|
|
switch target.(type) {
|
|
case CircleScanner:
|
|
return scanPlanTextAnyToCircleScanner{}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c CircleCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
|
return codecDecodeToTextFormat(c, m, oid, format, src)
|
|
}
|
|
|
|
func (c CircleCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
|
if src == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var circle Circle
|
|
err := codecScan(c, m, oid, format, src, &circle)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return circle, nil
|
|
}
|
|
|
|
type scanPlanBinaryCircleToCircleScanner struct{}
|
|
|
|
func (scanPlanBinaryCircleToCircleScanner) Scan(src []byte, dst any) error {
|
|
scanner := (dst).(CircleScanner)
|
|
|
|
if src == nil {
|
|
return scanner.ScanCircle(Circle{})
|
|
}
|
|
|
|
if len(src) != 24 {
|
|
return fmt.Errorf("invalid length for Circle: %v", len(src))
|
|
}
|
|
|
|
x := binary.BigEndian.Uint64(src)
|
|
y := binary.BigEndian.Uint64(src[8:])
|
|
r := binary.BigEndian.Uint64(src[16:])
|
|
|
|
return scanner.ScanCircle(Circle{
|
|
P: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
|
|
R: math.Float64frombits(r),
|
|
Valid: true,
|
|
})
|
|
}
|
|
|
|
type scanPlanTextAnyToCircleScanner struct{}
|
|
|
|
func (scanPlanTextAnyToCircleScanner) Scan(src []byte, dst any) error {
|
|
scanner := (dst).(CircleScanner)
|
|
|
|
if src == nil {
|
|
return scanner.ScanCircle(Circle{})
|
|
}
|
|
|
|
if len(src) < 9 {
|
|
return fmt.Errorf("invalid length for Circle: %v", len(src))
|
|
}
|
|
|
|
str := string(src[2:])
|
|
end := strings.IndexByte(str, ',')
|
|
x, err := strconv.ParseFloat(str[:end], 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
str = str[end+1:]
|
|
end = strings.IndexByte(str, ')')
|
|
|
|
y, err := strconv.ParseFloat(str[:end], 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
str = str[end+2 : len(str)-1]
|
|
|
|
r, err := strconv.ParseFloat(str, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return scanner.ScanCircle(Circle{P: Vec2{x, y}, R: r, Valid: true})
|
|
}
|