...

Source file src/github.com/jackc/pgtype/point.go

Documentation: github.com/jackc/pgtype

     1  package pgtype
     2  
     3  import (
     4  	"bytes"
     5  	"database/sql/driver"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"math"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/jackc/pgio"
    13  )
    14  
    15  type Vec2 struct {
    16  	X float64
    17  	Y float64
    18  }
    19  
    20  type Point struct {
    21  	P      Vec2
    22  	Status Status
    23  }
    24  
    25  func (dst *Point) Set(src interface{}) error {
    26  	if src == nil {
    27  		dst.Status = Null
    28  		return nil
    29  	}
    30  	err := fmt.Errorf("cannot convert %v to Point", src)
    31  	var p *Point
    32  	switch value := src.(type) {
    33  	case string:
    34  		p, err = parsePoint([]byte(value))
    35  	case []byte:
    36  		p, err = parsePoint(value)
    37  	default:
    38  		return err
    39  	}
    40  	if err != nil {
    41  		return err
    42  	}
    43  	*dst = *p
    44  	return nil
    45  }
    46  
    47  func parsePoint(src []byte) (*Point, error) {
    48  	if src == nil || bytes.Compare(src, []byte("null")) == 0 {
    49  		return &Point{Status: Null}, nil
    50  	}
    51  
    52  	if len(src) < 5 {
    53  		return nil, fmt.Errorf("invalid length for point: %v", len(src))
    54  	}
    55  	if src[0] == '"' && src[len(src)-1] == '"' {
    56  		src = src[1 : len(src)-1]
    57  	}
    58  	parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
    59  	if len(parts) < 2 {
    60  		return nil, fmt.Errorf("invalid format for point")
    61  	}
    62  
    63  	x, err := strconv.ParseFloat(parts[0], 64)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	y, err := strconv.ParseFloat(parts[1], 64)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	return &Point{P: Vec2{x, y}, Status: Present}, nil
    74  }
    75  
    76  func (dst Point) Get() interface{} {
    77  	switch dst.Status {
    78  	case Present:
    79  		return dst
    80  	case Null:
    81  		return nil
    82  	default:
    83  		return dst.Status
    84  	}
    85  }
    86  
    87  func (src *Point) AssignTo(dst interface{}) error {
    88  	return fmt.Errorf("cannot assign %v to %T", src, dst)
    89  }
    90  
    91  func (dst *Point) DecodeText(ci *ConnInfo, src []byte) error {
    92  	if src == nil {
    93  		*dst = Point{Status: Null}
    94  		return nil
    95  	}
    96  
    97  	if len(src) < 5 {
    98  		return fmt.Errorf("invalid length for point: %v", len(src))
    99  	}
   100  
   101  	parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
   102  	if len(parts) < 2 {
   103  		return fmt.Errorf("invalid format for point")
   104  	}
   105  
   106  	x, err := strconv.ParseFloat(parts[0], 64)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	y, err := strconv.ParseFloat(parts[1], 64)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	*dst = Point{P: Vec2{x, y}, Status: Present}
   117  	return nil
   118  }
   119  
   120  func (dst *Point) DecodeBinary(ci *ConnInfo, src []byte) error {
   121  	if src == nil {
   122  		*dst = Point{Status: Null}
   123  		return nil
   124  	}
   125  
   126  	if len(src) != 16 {
   127  		return fmt.Errorf("invalid length for point: %v", len(src))
   128  	}
   129  
   130  	x := binary.BigEndian.Uint64(src)
   131  	y := binary.BigEndian.Uint64(src[8:])
   132  
   133  	*dst = Point{
   134  		P:      Vec2{math.Float64frombits(x), math.Float64frombits(y)},
   135  		Status: Present,
   136  	}
   137  	return nil
   138  }
   139  
   140  func (src Point) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
   141  	switch src.Status {
   142  	case Null:
   143  		return nil, nil
   144  	case Undefined:
   145  		return nil, errUndefined
   146  	}
   147  
   148  	return append(buf, fmt.Sprintf(`(%s,%s)`,
   149  		strconv.FormatFloat(src.P.X, 'f', -1, 64),
   150  		strconv.FormatFloat(src.P.Y, 'f', -1, 64),
   151  	)...), nil
   152  }
   153  
   154  func (src Point) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
   155  	switch src.Status {
   156  	case Null:
   157  		return nil, nil
   158  	case Undefined:
   159  		return nil, errUndefined
   160  	}
   161  
   162  	buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X))
   163  	buf = pgio.AppendUint64(buf, math.Float64bits(src.P.Y))
   164  	return buf, nil
   165  }
   166  
   167  // Scan implements the database/sql Scanner interface.
   168  func (dst *Point) Scan(src interface{}) error {
   169  	if src == nil {
   170  		*dst = Point{Status: Null}
   171  		return nil
   172  	}
   173  
   174  	switch src := src.(type) {
   175  	case string:
   176  		return dst.DecodeText(nil, []byte(src))
   177  	case []byte:
   178  		srcCopy := make([]byte, len(src))
   179  		copy(srcCopy, src)
   180  		return dst.DecodeText(nil, srcCopy)
   181  	}
   182  
   183  	return fmt.Errorf("cannot scan %T", src)
   184  }
   185  
   186  // Value implements the database/sql/driver Valuer interface.
   187  func (src Point) Value() (driver.Value, error) {
   188  	return EncodeValueText(src)
   189  }
   190  
   191  func (src Point) MarshalJSON() ([]byte, error) {
   192  	switch src.Status {
   193  	case Present:
   194  		var buff bytes.Buffer
   195  		buff.WriteByte('"')
   196  		buff.WriteString(fmt.Sprintf("(%g,%g)", src.P.X, src.P.Y))
   197  		buff.WriteByte('"')
   198  		return buff.Bytes(), nil
   199  	case Null:
   200  		return []byte("null"), nil
   201  	case Undefined:
   202  		return nil, errUndefined
   203  	}
   204  	return nil, errBadStatus
   205  }
   206  
   207  func (dst *Point) UnmarshalJSON(point []byte) error {
   208  	p, err := parsePoint(point)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	*dst = *p
   213  	return nil
   214  }
   215  

View as plain text