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  }