github.com/aavshr/aws-sdk-go@v1.41.3/private/protocol/idempotency.go (about)

     1  package protocol
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"reflect"
     7  )
     8  
     9  // RandReader is the random reader the protocol package will use to read
    10  // random bytes from. This is exported for testing, and should not be used.
    11  var RandReader = rand.Reader
    12  
    13  const idempotencyTokenFillTag = `idempotencyToken`
    14  
    15  // CanSetIdempotencyToken returns true if the struct field should be
    16  // automatically populated with a Idempotency token.
    17  //
    18  // Only *string and string type fields that are tagged with idempotencyToken
    19  // which are not already set can be auto filled.
    20  func CanSetIdempotencyToken(v reflect.Value, f reflect.StructField) bool {
    21  	switch u := v.Interface().(type) {
    22  	// To auto fill an Idempotency token the field must be a string,
    23  	// tagged for auto fill, and have a zero value.
    24  	case *string:
    25  		return u == nil && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
    26  	case string:
    27  		return len(u) == 0 && len(f.Tag.Get(idempotencyTokenFillTag)) != 0
    28  	}
    29  
    30  	return false
    31  }
    32  
    33  // GetIdempotencyToken returns a randomly generated idempotency token.
    34  func GetIdempotencyToken() string {
    35  	b := make([]byte, 16)
    36  	RandReader.Read(b)
    37  
    38  	return UUIDVersion4(b)
    39  }
    40  
    41  // SetIdempotencyToken will set the value provided with a Idempotency Token.
    42  // Given that the value can be set. Will panic if value is not setable.
    43  func SetIdempotencyToken(v reflect.Value) {
    44  	if v.Kind() == reflect.Ptr {
    45  		if v.IsNil() && v.CanSet() {
    46  			v.Set(reflect.New(v.Type().Elem()))
    47  		}
    48  		v = v.Elem()
    49  	}
    50  	v = reflect.Indirect(v)
    51  
    52  	if !v.CanSet() {
    53  		panic(fmt.Sprintf("unable to set idempotnecy token %v", v))
    54  	}
    55  
    56  	b := make([]byte, 16)
    57  	_, err := rand.Read(b)
    58  	if err != nil {
    59  		// TODO handle error
    60  		return
    61  	}
    62  
    63  	v.Set(reflect.ValueOf(UUIDVersion4(b)))
    64  }
    65  
    66  // UUIDVersion4 returns a Version 4 random UUID from the byte slice provided
    67  func UUIDVersion4(u []byte) string {
    68  	// https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
    69  	// 13th character is "4"
    70  	u[6] = (u[6] | 0x40) & 0x4F
    71  	// 17th character is "8", "9", "a", or "b"
    72  	u[8] = (u[8] | 0x80) & 0xBF
    73  
    74  	return fmt.Sprintf(`%X-%X-%X-%X-%X`, u[0:4], u[4:6], u[6:8], u[8:10], u[10:])
    75  }