summaryrefslogtreecommitdiff
path: root/random/random.go
diff options
context:
space:
mode:
authorDave Henderson <dhenderson@gmail.com>2019-02-28 20:34:31 -0500
committerDave Henderson <dhenderson@gmail.com>2019-03-17 13:00:23 -0400
commit0347dd1f69480206fb23ff754cbf5b459bf06817 (patch)
tree91cc47762c0cb13c53d66d969fda29c511523a5a /random/random.go
parent13c1c4bf680acb6c9421c30c72ec21a626897b70 (diff)
New random namespace for generating random strings and numbers
Signed-off-by: Dave Henderson <dhenderson@gmail.com>
Diffstat (limited to 'random/random.go')
-rw-r--r--random/random.go109
1 files changed, 109 insertions, 0 deletions
diff --git a/random/random.go b/random/random.go
new file mode 100644
index 00000000..58a44d7d
--- /dev/null
+++ b/random/random.go
@@ -0,0 +1,109 @@
+package random
+
+import (
+ "math"
+ "math/rand"
+ "regexp"
+ "time"
+ "unicode"
+
+ "github.com/pkg/errors"
+)
+
+// Rnd -
+var Rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+// Default set, matches "[a-zA-Z0-9_.-]"
+const defaultSet = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
+
+// StringRE - Generate a random string that matches a given regular
+// expression. Defaults to "[a-zA-Z0-9_.-]"
+func StringRE(count int, match string) (r string, err error) {
+ var chars []rune
+ chars = []rune(defaultSet)
+ if match != "" {
+ chars, err = matchChars(match)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ return rndString(count, chars)
+}
+
+// StringBounds returns a random string of characters with a codepoint
+// between the lower and upper bounds. Only valid characters are returned
+// and if a range is given where no valid characters can be found, an error
+// will be returned.
+func StringBounds(count int, lower, upper rune) (r string, err error) {
+ chars := filterRange(lower, upper)
+ if len(chars) == 0 {
+ return "", errors.Errorf("No printable codepoints found between U%#q and U%#q.", lower, upper)
+ }
+ return rndString(count, chars)
+}
+
+// produce a string containing a random selection of given characters
+func rndString(count int, chars []rune) (string, error) {
+ s := make([]rune, count)
+ for i := range s {
+ s[i] = chars[Rnd.Intn(len(chars))]
+ }
+ return string(s), nil
+}
+
+func filterRange(lower, upper rune) []rune {
+ out := []rune{}
+ for r := lower; r <= upper; r++ {
+ if unicode.IsGraphic(r) {
+ out = append(out, r)
+ }
+ }
+ return out
+}
+
+func matchChars(match string) ([]rune, error) {
+ r, err := regexp.Compile(match)
+ if err != nil {
+ return nil, err
+ }
+ candidates := filterRange(0, unicode.MaxRune)
+ out := []rune{}
+ for _, c := range candidates {
+ if r.MatchString(string(c)) {
+ out = append(out, c)
+ }
+ }
+ return out, nil
+}
+
+// Item -
+func Item(items []interface{}) (interface{}, error) {
+ if len(items) == 0 {
+ return nil, errors.Errorf("expected a non-empty array or slice")
+ }
+ if len(items) == 1 {
+ return items[0], nil
+ }
+ n := Rnd.Intn(len(items))
+ return items[n], nil
+}
+
+// Number -
+func Number(min, max int64) (int64, error) {
+ if min > max {
+ return 0, errors.Errorf("min must not be greater than max (was %d, %d)", min, max)
+ }
+ if min == math.MinInt64 {
+ min++
+ }
+ if max-min >= (math.MaxInt64 >> 1) {
+ return 0, errors.Errorf("spread between min and max too high - must not be greater than 63-bit maximum (%d - %d = %d)", max, min, max-min)
+ }
+ return Rnd.Int63n(max-min+1) + min, nil
+}
+
+// Float - For now this is really just a wrapper around `rand.Float64`
+func Float(min, max float64) (float64, error) {
+ return min + Rnd.Float64()*(max-min), nil
+}