github.com/blend/go-sdk@v1.20220411.3/uuid/uuid.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package uuid
     9  
    10  import (
    11  	"bytes"
    12  	"database/sql/driver"
    13  	"encoding/hex"
    14  	"encoding/json"
    15  	"fmt"
    16  	"strings"
    17  
    18  	"github.com/blend/go-sdk/ex"
    19  )
    20  
    21  // ErrInvalidScanSource is an error returned by scan.
    22  const (
    23  	ErrInvalidScanSource ex.Class = "uuid: invalid scan source"
    24  )
    25  
    26  // Zero is the empty UUID.
    27  var (
    28  	Zero UUID = Empty()
    29  )
    30  
    31  // Empty returns an empty uuid.
    32  func Empty() UUID {
    33  	return UUID(make([]byte, 16))
    34  }
    35  
    36  // UUID represents a unique identifier conforming to the RFC 4122 standard.
    37  // UUIDs are a fixed 128bit (16 byte) binary blob.
    38  type UUID []byte
    39  
    40  // Equal returns if a uuid is equal to another uuid.
    41  func (uuid UUID) Equal(other UUID) bool {
    42  	return bytes.Equal(uuid[0:], other[0:])
    43  }
    44  
    45  // Compare returns a comparison between the two uuids.
    46  func (uuid UUID) Compare(other UUID) int {
    47  	return bytes.Compare(uuid[0:], other[0:])
    48  }
    49  
    50  // ToFullString returns a "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" hex representation of a uuid.
    51  func (uuid UUID) ToFullString() string {
    52  	if len(uuid) == 0 {
    53  		return ""
    54  	}
    55  	b := []byte(uuid)
    56  	return fmt.Sprintf(
    57  		"%08x-%04x-%04x-%04x-%012x",
    58  		b[:4], b[4:6], b[6:8], b[8:10], b[10:],
    59  	)
    60  }
    61  
    62  // ToShortString returns a hex representation of the uuid.
    63  func (uuid UUID) ToShortString() string {
    64  	return hex.EncodeToString([]byte(uuid))
    65  }
    66  
    67  // String is an alias for `ToShortString`.
    68  func (uuid UUID) String() string {
    69  	return hex.EncodeToString([]byte(uuid))
    70  }
    71  
    72  // Version returns the version byte of a uuid.
    73  func (uuid UUID) Version() byte {
    74  	return uuid[6] >> 4
    75  }
    76  
    77  // Format allows for conditional expansion in printf statements
    78  // based on the token and flags used.
    79  func (uuid UUID) Format(s fmt.State, verb rune) {
    80  	switch verb {
    81  	case 'v':
    82  		if s.Flag('+') {
    83  			fmt.Fprint(s, uuid.ToFullString())
    84  			return
    85  		}
    86  		fmt.Fprint(s, uuid.String())
    87  	case 's':
    88  		fmt.Fprint(s, uuid.String())
    89  	case 'q':
    90  		fmt.Fprintf(s, "%b", uuid.Version())
    91  	}
    92  }
    93  
    94  // IsZero returns if the uuid is unset.
    95  func (uuid UUID) IsZero() bool {
    96  	if len(uuid) == 0 {
    97  		return true
    98  	}
    99  	return bytes.Equal(uuid, Zero)
   100  }
   101  
   102  // IsV4 returns true iff uuid has version number 4, variant number 2, and length 16 bytes
   103  func (uuid UUID) IsV4() bool {
   104  	if len(uuid) != 16 {
   105  		return false
   106  	}
   107  	// check that version number is 4
   108  	if (uuid[6]&0xf0)^0x40 != 0 {
   109  		return false
   110  	}
   111  	// check that variant is 2
   112  	return (uuid[8]&0xc0)^0x80 == 0
   113  }
   114  
   115  // Marshal implements bytes marshal.
   116  func (uuid UUID) Marshal() ([]byte, error) {
   117  	if len(uuid) == 0 {
   118  		return nil, nil
   119  	}
   120  	return []byte(uuid), nil
   121  }
   122  
   123  // MarshalTo marshals the uuid to a buffer.
   124  func (uuid UUID) MarshalTo(data []byte) (n int, err error) {
   125  	if len(uuid) == 0 {
   126  		return 0, nil
   127  	}
   128  	copy(data, uuid)
   129  	return 16, nil
   130  }
   131  
   132  // Unmarshal implements bytes unmarshal.
   133  func (uuid *UUID) Unmarshal(data []byte) error {
   134  	if len(data) == 0 {
   135  		return nil
   136  	}
   137  	var id UUID = Empty()
   138  	copy(id, data)
   139  	*uuid = id
   140  	return nil
   141  }
   142  
   143  // Size returns the size of the uuid.
   144  func (uuid *UUID) Size() int {
   145  	if uuid == nil {
   146  		return 0
   147  	}
   148  	if len(*uuid) == 0 {
   149  		return 0
   150  	}
   151  	return 16
   152  }
   153  
   154  // MarshalJSON marshals a uuid as json.
   155  func (uuid UUID) MarshalJSON() ([]byte, error) {
   156  	return json.Marshal(uuid.ToFullString())
   157  }
   158  
   159  // UnmarshalJSON unmarshals a uuid from json.
   160  func (uuid *UUID) UnmarshalJSON(corpus []byte) error {
   161  	if len(*uuid) == 0 {
   162  		(*uuid) = Empty()
   163  	}
   164  	raw := strings.TrimSpace(string(corpus))
   165  	raw = strings.TrimPrefix(raw, "\"")
   166  	raw = strings.TrimSuffix(raw, "\"")
   167  	return ParseExisting(uuid, raw)
   168  }
   169  
   170  // MarshalYAML marshals a uuid as yaml.
   171  func (uuid UUID) MarshalYAML() (interface{}, error) {
   172  	return uuid.ToFullString(), nil
   173  }
   174  
   175  // UnmarshalYAML unmarshals a uuid from yaml.
   176  func (uuid *UUID) UnmarshalYAML(unmarshaler func(interface{}) error) error {
   177  	if len(*uuid) == 0 {
   178  		(*uuid) = Empty()
   179  	}
   180  
   181  	var corpus string
   182  	if err := unmarshaler(&corpus); err != nil {
   183  		return err
   184  	}
   185  
   186  	raw := strings.TrimSpace(string(corpus))
   187  	raw = strings.TrimPrefix(raw, "\"")
   188  	raw = strings.TrimSuffix(raw, "\"")
   189  	return ParseExisting(uuid, raw)
   190  }
   191  
   192  // Scan scans a uuid from a db value.
   193  func (uuid *UUID) Scan(src interface{}) error {
   194  	if len(*uuid) == 0 {
   195  		(*uuid) = Empty()
   196  	}
   197  	switch v := src.(type) {
   198  	case string:
   199  		return ParseExisting(uuid, v)
   200  	case []byte:
   201  		return ParseExisting(uuid, string(v))
   202  	default:
   203  		return ex.New(ErrInvalidScanSource, ex.OptMessagef("scan type: %T", src))
   204  	}
   205  }
   206  
   207  // Value returns a sql driver value.
   208  func (uuid UUID) Value() (driver.Value, error) {
   209  	if uuid == nil || len(uuid) == 0 {
   210  		return nil, nil
   211  	}
   212  	return uuid.String(), nil
   213  }