go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/uuid/parse.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package uuid
     9  
    10  import "errors"
    11  
    12  // Error Classes
    13  const (
    14  	ErrParseInvalidUUIDInput = "parse uuid: existing uuid is invalid"
    15  	ErrParseInvalidLength    = "parse uuid: input is an invalid length"
    16  	ErrParseIllegalCharacter = "parse uuid: illegal character"
    17  )
    18  
    19  // MustParse parses a uuid and will panic if there is an error.
    20  func MustParse(corpus string) (uuid UUID) {
    21  	if err := ParseInto(&uuid, corpus); err != nil {
    22  		panic(err)
    23  	}
    24  	return
    25  }
    26  
    27  // Parse parses a uuidv4 from a given string.
    28  // valid forms are:
    29  // - {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
    30  // - xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    31  // - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    32  func Parse(corpus string) (uuid UUID, err error) {
    33  	err = ParseInto(&uuid, corpus)
    34  	return
    35  }
    36  
    37  // ParseInto parses into an existing UUID.
    38  func ParseInto(uuid *UUID, corpus string) error {
    39  	corpusLen := len(corpus)
    40  	if corpusLen == 0 {
    41  		return nil
    42  	}
    43  	if corpusLen%2 == 1 {
    44  		return errors.New(ErrParseInvalidLength)
    45  	}
    46  
    47  	var data = []byte(corpus)
    48  	var c byte
    49  	hex := [2]byte{}
    50  	var hexChar byte
    51  	var isHexChar bool
    52  	var hexIndex, uuidIndex, di int
    53  
    54  	for i := 0; i < len(data); i++ {
    55  		c = data[i]
    56  		if c == '{' && i == 0 {
    57  			continue
    58  		}
    59  		if c == '{' {
    60  			return errors.New(ErrParseIllegalCharacter)
    61  		}
    62  		if c == '}' && i != len(data)-1 {
    63  			return errors.New(ErrParseIllegalCharacter)
    64  		}
    65  		if c == '}' {
    66  			continue
    67  		}
    68  
    69  		if c == '-' && !(di == 8 || di == 12 || di == 16 || di == 20) {
    70  			return errors.New(ErrParseIllegalCharacter)
    71  		}
    72  		if c == '-' {
    73  			continue
    74  		}
    75  
    76  		hexChar, isHexChar = fromHexChar(c)
    77  		if !isHexChar {
    78  			return errors.New(ErrParseIllegalCharacter)
    79  		}
    80  
    81  		hex[hexIndex] = hexChar
    82  		if hexIndex == 1 {
    83  			if uuidIndex >= 16 {
    84  				return errors.New(ErrParseInvalidLength)
    85  			}
    86  			(*uuid)[uuidIndex] = hex[0]<<4 | hex[1]
    87  			uuidIndex++
    88  
    89  			hexIndex = 0
    90  		} else {
    91  			hexIndex++
    92  		}
    93  		di++
    94  	}
    95  	if uuidIndex != 16 {
    96  		return errors.New(ErrParseInvalidLength)
    97  	}
    98  	return nil
    99  }
   100  
   101  func fromHexChar(c byte) (byte, bool) {
   102  	switch {
   103  	case '0' <= c && c <= '9':
   104  		return c - '0', true
   105  	case 'a' <= c && c <= 'f':
   106  		return c - 'a' + 10, true
   107  	case 'A' <= c && c <= 'F':
   108  		return c - 'A' + 10, true
   109  	}
   110  
   111  	return 0, false
   112  }