monzero/vendor/github.com/jackc/pgx/v5/pgtype/uuid.go
Gibheer 6ea4d2c82d switch from github.com/lib/pq to github.com/jackc/pgx/v5
lib/pq is out of maintenance for some time now, so switch to the newer
more active library. Looks like it finally stabilized after a long time.
2024-09-05 19:38:25 +02:00

282 lines
5.5 KiB
Go

package pgtype
import (
"bytes"
"database/sql/driver"
"encoding/hex"
"fmt"
)
type UUIDScanner interface {
ScanUUID(v UUID) error
}
type UUIDValuer interface {
UUIDValue() (UUID, error)
}
type UUID struct {
Bytes [16]byte
Valid bool
}
func (b *UUID) ScanUUID(v UUID) error {
*b = v
return nil
}
func (b UUID) UUIDValue() (UUID, error) {
return b, nil
}
// parseUUID converts a string UUID in standard form to a byte array.
func parseUUID(src string) (dst [16]byte, err error) {
switch len(src) {
case 36:
src = src[0:8] + src[9:13] + src[14:18] + src[19:23] + src[24:]
case 32:
// dashes already stripped, assume valid
default:
// assume invalid.
return dst, fmt.Errorf("cannot parse UUID %v", src)
}
buf, err := hex.DecodeString(src)
if err != nil {
return dst, err
}
copy(dst[:], buf)
return dst, err
}
// encodeUUID converts a uuid byte array to UUID standard string form.
func encodeUUID(src [16]byte) string {
var buf [36]byte
hex.Encode(buf[0:8], src[:4])
buf[8] = '-'
hex.Encode(buf[9:13], src[4:6])
buf[13] = '-'
hex.Encode(buf[14:18], src[6:8])
buf[18] = '-'
hex.Encode(buf[19:23], src[8:10])
buf[23] = '-'
hex.Encode(buf[24:], src[10:])
return string(buf[:])
}
// Scan implements the database/sql Scanner interface.
func (dst *UUID) Scan(src any) error {
if src == nil {
*dst = UUID{}
return nil
}
switch src := src.(type) {
case string:
buf, err := parseUUID(src)
if err != nil {
return err
}
*dst = UUID{Bytes: buf, Valid: true}
return nil
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src UUID) Value() (driver.Value, error) {
if !src.Valid {
return nil, nil
}
return encodeUUID(src.Bytes), nil
}
func (src UUID) MarshalJSON() ([]byte, error) {
if !src.Valid {
return []byte("null"), nil
}
var buff bytes.Buffer
buff.WriteByte('"')
buff.WriteString(encodeUUID(src.Bytes))
buff.WriteByte('"')
return buff.Bytes(), nil
}
func (dst *UUID) UnmarshalJSON(src []byte) error {
if bytes.Equal(src, []byte("null")) {
*dst = UUID{}
return nil
}
if len(src) != 38 {
return fmt.Errorf("invalid length for UUID: %v", len(src))
}
buf, err := parseUUID(string(src[1 : len(src)-1]))
if err != nil {
return err
}
*dst = UUID{Bytes: buf, Valid: true}
return nil
}
type UUIDCodec struct{}
func (UUIDCodec) FormatSupported(format int16) bool {
return format == TextFormatCode || format == BinaryFormatCode
}
func (UUIDCodec) PreferredFormat() int16 {
return BinaryFormatCode
}
func (UUIDCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
if _, ok := value.(UUIDValuer); !ok {
return nil
}
switch format {
case BinaryFormatCode:
return encodePlanUUIDCodecBinaryUUIDValuer{}
case TextFormatCode:
return encodePlanUUIDCodecTextUUIDValuer{}
}
return nil
}
type encodePlanUUIDCodecBinaryUUIDValuer struct{}
func (encodePlanUUIDCodecBinaryUUIDValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
uuid, err := value.(UUIDValuer).UUIDValue()
if err != nil {
return nil, err
}
if !uuid.Valid {
return nil, nil
}
return append(buf, uuid.Bytes[:]...), nil
}
type encodePlanUUIDCodecTextUUIDValuer struct{}
func (encodePlanUUIDCodecTextUUIDValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
uuid, err := value.(UUIDValuer).UUIDValue()
if err != nil {
return nil, err
}
if !uuid.Valid {
return nil, nil
}
return append(buf, encodeUUID(uuid.Bytes)...), nil
}
func (UUIDCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
switch format {
case BinaryFormatCode:
switch target.(type) {
case UUIDScanner:
return scanPlanBinaryUUIDToUUIDScanner{}
case TextScanner:
return scanPlanBinaryUUIDToTextScanner{}
}
case TextFormatCode:
switch target.(type) {
case UUIDScanner:
return scanPlanTextAnyToUUIDScanner{}
}
}
return nil
}
type scanPlanBinaryUUIDToUUIDScanner struct{}
func (scanPlanBinaryUUIDToUUIDScanner) Scan(src []byte, dst any) error {
scanner := (dst).(UUIDScanner)
if src == nil {
return scanner.ScanUUID(UUID{})
}
if len(src) != 16 {
return fmt.Errorf("invalid length for UUID: %v", len(src))
}
uuid := UUID{Valid: true}
copy(uuid.Bytes[:], src)
return scanner.ScanUUID(uuid)
}
type scanPlanBinaryUUIDToTextScanner struct{}
func (scanPlanBinaryUUIDToTextScanner) Scan(src []byte, dst any) error {
scanner := (dst).(TextScanner)
if src == nil {
return scanner.ScanText(Text{})
}
if len(src) != 16 {
return fmt.Errorf("invalid length for UUID: %v", len(src))
}
var buf [16]byte
copy(buf[:], src)
return scanner.ScanText(Text{String: encodeUUID(buf), Valid: true})
}
type scanPlanTextAnyToUUIDScanner struct{}
func (scanPlanTextAnyToUUIDScanner) Scan(src []byte, dst any) error {
scanner := (dst).(UUIDScanner)
if src == nil {
return scanner.ScanUUID(UUID{})
}
buf, err := parseUUID(string(src))
if err != nil {
return err
}
return scanner.ScanUUID(UUID{Bytes: buf, Valid: true})
}
func (c UUIDCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
if src == nil {
return nil, nil
}
var uuid UUID
err := codecScan(c, m, oid, format, src, &uuid)
if err != nil {
return nil, err
}
return encodeUUID(uuid.Bytes), nil
}
func (c UUIDCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
if src == nil {
return nil, nil
}
var uuid UUID
err := codecScan(c, m, oid, format, src, &uuid)
if err != nil {
return nil, err
}
return uuid.Bytes, nil
}