package pgtype
import (
)
type Vec2 struct {
X float64
Y float64
}
type PointScanner interface {
ScanPoint(v Point) error
}
type PointValuer interface {
PointValue() (Point, error)
}
type Point struct {
P Vec2
Valid bool
}
func ( *Point) ( Point) error {
* =
return nil
}
func ( Point) () (Point, error) {
return , nil
}
func ( []byte) (*Point, error) {
if == nil || bytes.Equal(, []byte("null")) {
return &Point{}, nil
}
if len() < 5 {
return nil, fmt.Errorf("invalid length for point: %v", len())
}
if [0] == '"' && [len()-1] == '"' {
= [1 : len()-1]
}
, , := strings.Cut(string([1:len()-1]), ",")
if ! {
return nil, fmt.Errorf("invalid format for point")
}
, := strconv.ParseFloat(, 64)
if != nil {
return nil,
}
, := strconv.ParseFloat(, 64)
if != nil {
return nil,
}
return &Point{P: Vec2{, }, Valid: true}, nil
}
func ( *Point) ( any) error {
if == nil {
* = Point{}
return nil
}
switch src := .(type) {
case string:
return scanPlanTextAnyToPointScanner{}.Scan([]byte(), )
}
return fmt.Errorf("cannot scan %T", )
}
func ( Point) () (driver.Value, error) {
if !.Valid {
return nil, nil
}
, := PointCodec{}.PlanEncode(nil, 0, TextFormatCode, ).Encode(, nil)
if != nil {
return nil,
}
return string(),
}
func ( Point) () ([]byte, error) {
if !.Valid {
return []byte("null"), nil
}
var bytes.Buffer
.WriteByte('"')
.WriteString(fmt.Sprintf("(%g,%g)", .P.X, .P.Y))
.WriteByte('"')
return .Bytes(), nil
}
func ( *Point) ( []byte) error {
, := parsePoint()
if != nil {
return
}
* = *
return nil
}
type PointCodec struct{}
func (PointCodec) ( int16) bool {
return == TextFormatCode || == BinaryFormatCode
}
func (PointCodec) () int16 {
return BinaryFormatCode
}
func (PointCodec) ( *Map, uint32, int16, any) EncodePlan {
if , := .(PointValuer); ! {
return nil
}
switch {
case BinaryFormatCode:
return encodePlanPointCodecBinary{}
case TextFormatCode:
return encodePlanPointCodecText{}
}
return nil
}
type encodePlanPointCodecBinary struct{}
func (encodePlanPointCodecBinary) ( any, []byte) ( []byte, error) {
, := .(PointValuer).PointValue()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
= pgio.AppendUint64(, math.Float64bits(.P.X))
= pgio.AppendUint64(, math.Float64bits(.P.Y))
return , nil
}
type encodePlanPointCodecText struct{}
func (encodePlanPointCodecText) ( any, []byte) ( []byte, error) {
, := .(PointValuer).PointValue()
if != nil {
return nil,
}
if !.Valid {
return nil, nil
}
return append(, fmt.Sprintf(`(%s,%s)`,
strconv.FormatFloat(.P.X, 'f', -1, 64),
strconv.FormatFloat(.P.Y, 'f', -1, 64),
)...), nil
}
func (PointCodec) ( *Map, uint32, int16, any) ScanPlan {
switch {
case BinaryFormatCode:
switch .(type) {
case PointScanner:
return scanPlanBinaryPointToPointScanner{}
}
case TextFormatCode:
switch .(type) {
case PointScanner:
return scanPlanTextAnyToPointScanner{}
}
}
return nil
}
func ( PointCodec) ( *Map, uint32, int16, []byte) (driver.Value, error) {
return codecDecodeToTextFormat(, , , , )
}
func ( PointCodec) ( *Map, uint32, int16, []byte) (any, error) {
if == nil {
return nil, nil
}
var Point
:= codecScan(, , , , , &)
if != nil {
return nil,
}
return , nil
}
type scanPlanBinaryPointToPointScanner struct{}
func (scanPlanBinaryPointToPointScanner) ( []byte, any) error {
:= ().(PointScanner)
if == nil {
return .ScanPoint(Point{})
}
if len() != 16 {
return fmt.Errorf("invalid length for point: %v", len())
}
:= binary.BigEndian.Uint64()
:= binary.BigEndian.Uint64([8:])
return .ScanPoint(Point{
P: Vec2{math.Float64frombits(), math.Float64frombits()},
Valid: true,
})
}
type scanPlanTextAnyToPointScanner struct{}
func (scanPlanTextAnyToPointScanner) ( []byte, any) error {
:= ().(PointScanner)
if == nil {
return .ScanPoint(Point{})
}
if len() < 5 {
return fmt.Errorf("invalid length for point: %v", len())
}
, , := strings.Cut(string([1:len()-1]), ",")
if ! {
return fmt.Errorf("invalid format for point")
}
, := strconv.ParseFloat(, 64)
if != nil {
return
}
, := strconv.ParseFloat(, 64)
if != nil {
return
}
return .ScanPoint(Point{P: Vec2{, }, Valid: true})
}