github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/uuid.go (about) 1 // Copyright 2020 DataStax 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datacodec 16 17 import ( 18 "github.com/datastax/go-cassandra-native-protocol/datatype" 19 "github.com/datastax/go-cassandra-native-protocol/primitive" 20 ) 21 22 // Uuid is a codec for the CQL uuid type. Out of better options available in Go's standard library, its 23 // preferred Go type is primitive.Uuid, but it can encode from and decode to []byte, [16]byte and string as well. 24 // When dealing with UUIDs in Go, consider using a high-level library such as Google's uuid package: 25 // https://pkg.go.dev/github.com/google/uuid. 26 var Uuid Codec = &uuidCodec{dataType: datatype.Uuid} 27 28 // Timeuuid is a codec for the CQL timeuuid type. Out of better options available in Go's standard library, its 29 // preferred Go type is primitive.Uuid, but it can encode from and decode to []byte, [16]byte and string as well. 30 // This codec does not actually enforce that user-provided UUIDs are time UUIDs; it is functionally equivalent to 31 // the codecs returned by NewUuid. 32 // When dealing with UUIDs in Go, consider using a high-level library such as Google's uuid package: 33 // https://pkg.go.dev/github.com/google/uuid. 34 var Timeuuid Codec = &uuidCodec{dataType: datatype.Timeuuid} 35 36 type uuidCodec struct { 37 dataType datatype.DataType 38 } 39 40 func (c *uuidCodec) DataType() datatype.DataType { 41 return c.dataType 42 } 43 44 func (c *uuidCodec) Encode(source interface{}, version primitive.ProtocolVersion) (dest []byte, err error) { 45 if dest, err = convertToUuidBytes(source); err != nil { 46 err = errCannotEncode(source, c.DataType(), version, err) 47 } 48 return 49 } 50 51 func (c *uuidCodec) Decode(source []byte, dest interface{}, version primitive.ProtocolVersion) (wasNull bool, err error) { 52 var val []byte 53 if val, wasNull, err = readUuid(source); err == nil { 54 err = convertFromUuidBytes(val, wasNull, dest) 55 } 56 if err != nil { 57 err = errCannotDecode(dest, c.DataType(), version, err) 58 } 59 return 60 } 61 62 func convertToUuidBytes(source interface{}) (val []byte, err error) { 63 switch s := source.(type) { 64 case primitive.UUID: 65 val = s.Bytes() 66 case *primitive.UUID: 67 if s != nil { 68 val = s.Bytes() 69 } 70 case []byte: 71 if len(s) != primitive.LengthOfUuid { 72 err = errWrongFixedLength(primitive.LengthOfUuid, len(s)) 73 } else { 74 val = s 75 } 76 case *[]byte: 77 if s != nil { 78 if len(*s) != primitive.LengthOfUuid { 79 err = errWrongFixedLength(primitive.LengthOfUuid, len(*s)) 80 } else { 81 val = *s 82 } 83 } 84 case [16]byte: 85 val = s[:] 86 case *[16]byte: 87 if s != nil { 88 val = (*s)[:] 89 } 90 case string: 91 var uuid *primitive.UUID 92 if uuid, err = primitive.ParseUuid(s); err == nil { 93 val = uuid.Bytes() 94 } 95 case *string: 96 if s != nil { 97 var uuid *primitive.UUID 98 if uuid, err = primitive.ParseUuid(*s); err == nil { 99 val = uuid.Bytes() 100 } 101 } 102 case nil: 103 default: 104 err = ErrConversionNotSupported 105 } 106 if err != nil { 107 err = errSourceConversionFailed(source, val, err) 108 } 109 return 110 } 111 112 func convertFromUuidBytes(val []byte, wasNull bool, dest interface{}) (err error) { 113 switch d := dest.(type) { 114 case *interface{}: 115 if d == nil { 116 err = ErrNilDestination 117 } else if wasNull { 118 *d = nil 119 } else { 120 u := primitive.UUID{} 121 copy(u[:], val) 122 *d = u 123 } 124 case *primitive.UUID: 125 if d == nil { 126 err = ErrNilDestination 127 } else if wasNull { 128 *d = primitive.UUID{} 129 } else { 130 copy((*d)[:], val) 131 } 132 case *[]byte: 133 if d == nil { 134 err = ErrNilDestination 135 } else if wasNull { 136 *d = nil 137 } else { 138 *d = val 139 } 140 case *[16]byte: 141 if d == nil { 142 err = ErrNilDestination 143 } else if wasNull { 144 *d = [16]byte{} 145 } else { 146 *d = [16]byte{} 147 copy((*d)[:], val) 148 } 149 case *string: 150 if d == nil { 151 err = ErrNilDestination 152 } else if wasNull { 153 *d = "" 154 } else { 155 var uuid primitive.UUID 156 copy(uuid[:], val) 157 *d = uuid.String() 158 } 159 default: 160 err = errDestinationInvalid(dest) 161 } 162 if err != nil { 163 err = errDestinationConversionFailed(val, dest, err) 164 } 165 return 166 } 167 168 // The below function is roughly equivalent to primitive.ReadUuid. 169 170 func readUuid(source []byte) (val []byte, wasNull bool, err error) { 171 length := len(source) 172 if length == 0 { 173 wasNull = true 174 } else if length != primitive.LengthOfUuid { 175 err = errWrongFixedLength(primitive.LengthOfUuid, length) 176 } else { 177 val = source 178 } 179 if err != nil { 180 err = errCannotRead(val, err) 181 } 182 return 183 }