github.com/hairyhenderson/gomplate/v3@v3.11.7/random/random.go (about)

     1  // Package random contains functions for generating random values
     2  package random
     3  
     4  import (
     5  	"fmt"
     6  	"math"
     7  	"math/rand"
     8  	"regexp"
     9  	"time"
    10  	"unicode"
    11  )
    12  
    13  func init() {
    14  	rand.Seed(time.Now().UnixNano())
    15  }
    16  
    17  // Default set, matches "[a-zA-Z0-9_.-]"
    18  const defaultSet = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
    19  
    20  // StringRE - Generate a random string that matches a given regular
    21  // expression. Defaults to "[a-zA-Z0-9_.-]"
    22  func StringRE(count int, match string) (r string, err error) {
    23  	chars := []rune(defaultSet)
    24  	if match != "" {
    25  		chars, err = matchChars(match)
    26  		if err != nil {
    27  			return "", err
    28  		}
    29  	}
    30  
    31  	return rndString(count, chars)
    32  }
    33  
    34  // StringBounds returns a random string of characters with a codepoint
    35  // between the lower and upper bounds. Only valid characters are returned
    36  // and if a range is given where no valid characters can be found, an error
    37  // will be returned.
    38  func StringBounds(count int, lower, upper rune) (r string, err error) {
    39  	chars := filterRange(lower, upper)
    40  	if len(chars) == 0 {
    41  		return "", fmt.Errorf("no printable codepoints found between U%#q and U%#q", lower, upper)
    42  	}
    43  	return rndString(count, chars)
    44  }
    45  
    46  // produce a string containing a random selection of given characters
    47  func rndString(count int, chars []rune) (string, error) {
    48  	s := make([]rune, count)
    49  	for i := range s {
    50  		//nolint:gosec
    51  		s[i] = chars[rand.Intn(len(chars))]
    52  	}
    53  	return string(s), nil
    54  }
    55  
    56  func filterRange(lower, upper rune) []rune {
    57  	out := []rune{}
    58  	for r := lower; r <= upper; r++ {
    59  		if unicode.IsGraphic(r) {
    60  			out = append(out, r)
    61  		}
    62  	}
    63  	return out
    64  }
    65  
    66  func matchChars(match string) ([]rune, error) {
    67  	r, err := regexp.Compile(match)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	candidates := filterRange(0, unicode.MaxRune)
    72  	out := []rune{}
    73  	for _, c := range candidates {
    74  		if r.MatchString(string(c)) {
    75  			out = append(out, c)
    76  		}
    77  	}
    78  	return out, nil
    79  }
    80  
    81  // Item -
    82  func Item(items []interface{}) (interface{}, error) {
    83  	if len(items) == 0 {
    84  		return nil, fmt.Errorf("expected a non-empty array or slice")
    85  	}
    86  	if len(items) == 1 {
    87  		return items[0], nil
    88  	}
    89  
    90  	//nolint:gosec
    91  	n := rand.Intn(len(items))
    92  	return items[n], nil
    93  }
    94  
    95  // Number -
    96  func Number(min, max int64) (int64, error) {
    97  	if min > max {
    98  		return 0, fmt.Errorf("min must not be greater than max (was %d, %d)", min, max)
    99  	}
   100  	if min == math.MinInt64 {
   101  		min++
   102  	}
   103  	if max-min >= (math.MaxInt64 >> 1) {
   104  		return 0, fmt.Errorf("spread between min and max too high - must not be greater than 63-bit maximum (%d - %d = %d)", max, min, max-min)
   105  	}
   106  
   107  	//nolint:gosec
   108  	return rand.Int63n(max-min+1) + min, nil
   109  }
   110  
   111  // Float - For now this is really just a wrapper around `rand.Float64`
   112  func Float(min, max float64) (float64, error) {
   113  	//nolint:gosec
   114  	return min + rand.Float64()*(max-min), nil
   115  }