From ba753079822fdad14c204f0bfd19131fecfdaf08 Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Sun, 15 Dec 2024 17:51:29 -0300 Subject: [PATCH 1/8] chore: fixed .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index e8bc2f3..3a70484 100644 --- a/.gitignore +++ b/.gitignore @@ -282,4 +282,3 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser - From 9a074c08e1270b624549a982237582cfb0f7084e Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Sun, 15 Dec 2024 17:56:36 -0300 Subject: [PATCH 2/8] chore: fixed .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3a70484..37ca91d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ go.work.sum # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff +.idea .idea/**/workspace.xml .idea/**/tasks.xml .idea/**/usage.statistics.xml From 822173b17bc24c1a2f92a74844db4bea172e8c4d Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Sun, 15 Dec 2024 17:59:12 -0300 Subject: [PATCH 3/8] chore: introduced pre-commit to ensure code quality and standardization. --- .pre-commit-config.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9698af8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/dnephin/pre-commit-golang + rev: master + hooks: + - id: go-fmt + - id: go-vet + - id: go-imports + - id: go-cyclo + args: [-over=15] + - id: validate-toml + - id: no-go-testing + - id: golangci-lint + - id: go-critic + - id: go-unit-tests + - id: go-build + - id: go-mod-tidy From 06060879f60285d100cb14474cfdf6538d5548fa Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Sun, 15 Dec 2024 18:03:45 -0300 Subject: [PATCH 4/8] feat: strrand library functions created All functions and documentation for all functions in the strrand library were created --- strrand.go | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 strrand.go diff --git a/strrand.go b/strrand.go new file mode 100644 index 0000000..adda8ee --- /dev/null +++ b/strrand.go @@ -0,0 +1,129 @@ +package strrand + +import ( + ibytes "bytes" + "crypto/rand" + "encoding/binary" +) + +const ( + // BinaryCharset defines a binary character set (0 and 1). + BinaryCharset = "01" + // OctalCharset defines an octal character set (0-7). + OctalCharset = "01234567" + // DecimalCharset defines a decimal character set (0-9). + DecimalCharset = "0123456789" + // HexadecimalCharset defines a hexadecimal character set (0-9, a-f). + HexadecimalCharset = "0123456789abcdef" +) + +const ( + // UppercaseCharset defines uppercase alphabetic characters (A-Z). + UppercaseCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + // LowercaseCharset defines lowercase alphabetic characters (a-z). + LowercaseCharset = "abcdefghijklmnopqrstuvwxyz" + // SpecialCharset defines special characters. + SpecialCharset = "!@#$%^&*()-_=+[]{}|;:',.<>?/`~" +) + +const ( + // AlphabetCharset combines uppercase and lowercase alphabetic characters. + AlphabetCharset = UppercaseCharset + LowercaseCharset + // Base62Charset defines a Base62 character set (0-9, A-Z, a-z). + Base62Charset = DecimalCharset + AlphabetCharset + // Base64Charset defines a Base64 character set (0-9, A-Z, a-z, +, /). + Base64Charset = Base62Charset + "+/" + // DefaultCharset defines a default character set (Base62 + special characters). + DefaultCharset = Base62Charset + SpecialCharset +) + +// Binary generates a random string of the specified length using the binary charset (01). +func Binary(length uint32) string { + return random(length, BinaryCharset) +} + +// Octal generates a random string of the specified length using the octal charset (01234567). +func Octal(length uint32) string { + return random(length, OctalCharset) +} + +// Decimal generates a random string of the specified length using the decimal charset (0123456789). +func Decimal(length uint32) string { + return random(length, DecimalCharset) +} + +// Hexadecimal generates a random string of the specified length using the hexadecimal charset (0123456789abcdef). +func Hexadecimal(length uint32) string { + return random(length, HexadecimalCharset) +} + +// CapitalLetters generates a random string of the specified length using uppercase letters. +func CapitalLetters(length uint32) string { + return random(length, UppercaseCharset) +} + +// LowercaseLetters generates a random string of the specified length using lowercase letters. +func LowercaseLetters(length uint32) string { + return random(length, LowercaseCharset) +} + +// SpecialLetters generates a random string of the specified length using special characters. +func SpecialLetters(length uint32) string { + return random(length, SpecialCharset) +} + +// Base62 generates a random string of the specified length using the base62 charset (0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz). +func Base62(length uint32) string { + return random(length, Base62Charset) +} + +// Base64 generates a random string of the specified length using the base64 charset (0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/). +func Base64(length uint32) string { + return random(length, Base64Charset) +} + +// Letters generates a random string of the specified length using both uppercase and lowercase letters. +func Letters(length uint32) string { + return random(length, AlphabetCharset) +} + +// DefaultString generates a random string of the specified length using the default charset (base62 + special characters). +func DefaultString(length uint32) string { + return random(length, DefaultCharset) +} + +// random generates a random string of the specified length using the provided charset. +func random(length uint32, charset string) string { + var buffer ibytes.Buffer + buffer.Grow(int(length)) + + charsetRune := []rune(charset) + charsetRuneLength := uint32(len(charsetRune)) + + for range length { + index := binary.BigEndian.Uint32(bytes(4)) % charsetRuneLength + buffer.WriteRune(charsetRune[index]) + } + + return buffer.String() +} + +// bytes generates a byte slice of the specified length filled with random data. +func bytes(length uint32) []byte { + b := make([]byte, length) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + return b +} + +// String generates a random string of the specified length using a custom charset if provided, +// otherwise, it uses the default charset. +func String(length uint32, customCharset ...string) string { + if len(customCharset) == 0 { + return DefaultString(length) + } + + return random(length, customCharset[0]) +} From eacf110ef93f8b48f29f8544563183e12d6eb2a7 Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Sun, 15 Dec 2024 22:23:57 -0300 Subject: [PATCH 5/8] fix: updated the length type the length type was updated from uint32 to int, as it could generate overflow errors --- strrand.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/strrand.go b/strrand.go index adda8ee..d43c889 100644 --- a/strrand.go +++ b/strrand.go @@ -38,64 +38,68 @@ const ( ) // Binary generates a random string of the specified length using the binary charset (01). -func Binary(length uint32) string { +func Binary(length int) string { return random(length, BinaryCharset) } // Octal generates a random string of the specified length using the octal charset (01234567). -func Octal(length uint32) string { +func Octal(length int) string { return random(length, OctalCharset) } // Decimal generates a random string of the specified length using the decimal charset (0123456789). -func Decimal(length uint32) string { +func Decimal(length int) string { return random(length, DecimalCharset) } // Hexadecimal generates a random string of the specified length using the hexadecimal charset (0123456789abcdef). -func Hexadecimal(length uint32) string { +func Hexadecimal(length int) string { return random(length, HexadecimalCharset) } // CapitalLetters generates a random string of the specified length using uppercase letters. -func CapitalLetters(length uint32) string { +func CapitalLetters(length int) string { return random(length, UppercaseCharset) } // LowercaseLetters generates a random string of the specified length using lowercase letters. -func LowercaseLetters(length uint32) string { +func LowercaseLetters(length int) string { return random(length, LowercaseCharset) } // SpecialLetters generates a random string of the specified length using special characters. -func SpecialLetters(length uint32) string { +func SpecialLetters(length int) string { return random(length, SpecialCharset) } // Base62 generates a random string of the specified length using the base62 charset (0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz). -func Base62(length uint32) string { +func Base62(length int) string { return random(length, Base62Charset) } // Base64 generates a random string of the specified length using the base64 charset (0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/). -func Base64(length uint32) string { +func Base64(length int) string { return random(length, Base64Charset) } // Letters generates a random string of the specified length using both uppercase and lowercase letters. -func Letters(length uint32) string { +func Letters(length int) string { return random(length, AlphabetCharset) } // DefaultString generates a random string of the specified length using the default charset (base62 + special characters). -func DefaultString(length uint32) string { +func DefaultString(length int) string { return random(length, DefaultCharset) } // random generates a random string of the specified length using the provided charset. -func random(length uint32, charset string) string { +func random(length int, charset string) string { + if length <= 0 { + return "" + } + var buffer ibytes.Buffer - buffer.Grow(int(length)) + buffer.Grow(length) charsetRune := []rune(charset) charsetRuneLength := uint32(len(charsetRune)) @@ -109,7 +113,7 @@ func random(length uint32, charset string) string { } // bytes generates a byte slice of the specified length filled with random data. -func bytes(length uint32) []byte { +func bytes(length int) []byte { b := make([]byte, length) _, err := rand.Read(b) if err != nil { @@ -120,7 +124,7 @@ func bytes(length uint32) []byte { // String generates a random string of the specified length using a custom charset if provided, // otherwise, it uses the default charset. -func String(length uint32, customCharset ...string) string { +func String(length int, customCharset ...string) string { if len(customCharset) == 0 { return DefaultString(length) } From 4e91a1cb917744302fa66ef686ab96e7c85d03f0 Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:26:15 -0300 Subject: [PATCH 6/8] test: library tests created Added comprehensive tests for all application methods, ensuring a high level of coverage and validation of error scenarios. --- .pre-commit-config.yaml | 4 +- strrand_test.go | 219 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 strrand_test.go diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9698af8..e043f2a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,13 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - repo: https://github.com/dnephin/pre-commit-golang - rev: master + rev: v0.5.1 hooks: - id: go-fmt - id: go-vet diff --git a/strrand_test.go b/strrand_test.go new file mode 100644 index 0000000..513e906 --- /dev/null +++ b/strrand_test.go @@ -0,0 +1,219 @@ +package strrand_test + +import ( + "fmt" + "math/rand/v2" + "regexp" + "testing" + + . "github.com/Diaszano/strrand" +) + +// TestPredefinedStringGenerators validates various string generator functions provided by the strrand package. +// Each generator is tested against its expected output format, including invalid and typical input lengths. +// +//gocyclo:ignore +func TestPredefinedStringGenerators(t *testing.T) { + // Define test cases for different string generator functions and their respective regex patterns. + testCases := map[string]struct { + f func(int) string // String generator function + regex *regexp.Regexp // Regex to validate the output + }{ + "Binary": {f: Binary, regex: regexp.MustCompile(`^(?i)[01]+$`)}, + "Octal": {f: Octal, regex: regexp.MustCompile(`^(?i)[0-7]+$`)}, + "Decimal": {f: Decimal, regex: regexp.MustCompile(`^(?i)[0-9]+$`)}, + "Hexadecimal": {f: Hexadecimal, regex: regexp.MustCompile(`^(?i)[0-9a-f]+$`)}, + "CapitalLetters": {f: CapitalLetters, regex: regexp.MustCompile(`^(?i)[A-Z]+$`)}, + "LowercaseLetters": {f: LowercaseLetters, regex: regexp.MustCompile(`^(?i)[a-z]+$`)}, + "SpecialLetters": {f: SpecialLetters, regex: regexp.MustCompile(`^(?i)[!@#$%^&*\(\)\-_=+\[\]{}|;:',.<>?/` + "`~]+$")}, + "Base62": {f: Base62, regex: regexp.MustCompile(`^(?i)[0-9A-Z]+$`)}, + "Base64": {f: Base64, regex: regexp.MustCompile(`^(?i)[0-9A-Za-z+/]+$`)}, + "Letters": {f: Letters, regex: regexp.MustCompile(`^(?i)[A-Za-z]+$`)}, + "DefaultString": {f: DefaultString, regex: regexp.MustCompile(`^(?i)[0-9A-Za-z!@#$%^&*\(\)\-_=+\[\]{}|;:',.<>?/` + "`~]+$")}, + } + + // Iterate through each test case. + for name, config := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + // Subtest: Validate edge cases for invalid input lengths. + t.Run("InvalidInputs", func(t *testing.T) { + t.Run("NegativeLength", func(t *testing.T) { + result := config.f(-1) + if len(result) != 0 { + t.Errorf("Negative length: expected empty string, got %q", result) + } + }) + + t.Run("ZeroLength", func(t *testing.T) { + result := config.f(0) + if len(result) != 0 { + t.Errorf("Zero length: expected empty string, got %q", result) + } + }) + }) + + // Subtest: Validate typical input lengths. + t.Run("TypicalLengths", func(t *testing.T) { + cases := []struct { + length int + }{ + {length: 4}, + {length: 16}, + {length: 1024}, + {length: 8192}, + {length: 32768}, + } + + for _, tc := range cases { + t.Run(fmt.Sprintf("Length-%d", tc.length), func(t *testing.T) { + t.Parallel() + result := config.f(tc.length) + + // Verify output length matches the input length. + if len(result) != tc.length { + t.Errorf("Expected length %d, got %d", tc.length, len(result)) + } + + // Validate the output format using regex. + if !config.regex.MatchString(result) { + t.Errorf("Result contains invalid characters: %q", result) + } + }) + } + }) + + // Subtest: Validate outputs for random input lengths. + for range 30 { + t.Run("RandomLength", func(t *testing.T) { + t.Parallel() + length := rand.IntN(100000) + result := config.f(length) + + // Verify output length matches the random input length. + if len(result) != length { + t.Errorf("Random length %d: expected length %d, got %d", length, length, len(result)) + } + + // Validate the output format using regex. + if length > 0 && !config.regex.MatchString(result) { + t.Errorf("Result contains invalid characters: %q", result) + } + }) + } + }) + } +} + +// TestString validates the behavior of the String function. +// +//gocyclo:ignore +func TestString(t *testing.T) { + // Test cases for the default charset. + t.Run("DefaultCharset", func(t *testing.T) { + cases := []struct { + length int + }{ + {length: -1}, // Invalid length + {length: 0}, // Zero length + {length: 4}, + {length: 16}, + {length: 1024}, + {length: 8192}, + {length: 32768}, + } + regex := regexp.MustCompile(`^(?i)[0-9A-Za-z!@#$%^&*\(\)\-_=+\[\]{}|;:',.<>?/` + "`~]+$") + + for _, tc := range cases { + t.Run(fmt.Sprintf("Length-%d", tc.length), func(t *testing.T) { + t.Parallel() + + result := String(tc.length) + + // Verify the output length matches the input length. + if tc.length <= 0 { + if len(result) != 0 { + t.Errorf("Expected empty string for length %d, got %q", tc.length, result) + } + + return + } + + if len(result) != tc.length { + t.Errorf("Expected length %d, got %d", tc.length, len(result)) + } + + // Validate the output using the regex for the default charset. + if !regex.MatchString(result) { + t.Errorf("Result contains invalid characters: %q", result) + } + }) + } + }) + + // Test cases for custom charsets. + t.Run("CustomCharset", func(t *testing.T) { + customCharset := "abc12345" + regex := regexp.MustCompile(`^[a-c1-5]*$`) + cases := []struct { + length int + }{ + {length: -1}, // Invalid length + {length: 0}, // Zero length + {length: 4}, + {length: 16}, + {length: 1024}, + {length: 8192}, + {length: 32768}, + } + + for _, tc := range cases { + t.Run(fmt.Sprintf("Length-%d", tc.length), func(t *testing.T) { + t.Parallel() + result := String(tc.length, customCharset) + + // Verify the output length matches the input length. + if tc.length <= 0 { + if len(result) != 0 { + t.Errorf("Expected empty string for length %d, got %q", tc.length, result) + } + + return + } + + if len(result) != tc.length { + t.Errorf("Expected length %d, got %d", tc.length, len(result)) + } + + // Validate the output using the regex for the custom charset. + if tc.length > 0 && !regex.MatchString(result) { + t.Errorf("Result contains invalid characters: %q", result) + } + }) + } + }) + + // Test random lengths with the default charset. + t.Run("RandomLengths", func(t *testing.T) { + regex := regexp.MustCompile(`^(?i)[0-9A-Za-z!@#$%^&*\(\)\-_=+\[\]{}|;:',.<>?/` + "`~]+$") + for range 30 { + t.Run("Random", func(t *testing.T) { + t.Parallel() + + length := rand.IntN(100000) + result := String(length) + + // Verify the output length matches the random input length. + if len(result) != length { + t.Errorf("Expected length %d, got %d", length, len(result)) + } + + // Validate the output using the regex for the default charset. + if length > 0 && !regex.MatchString(result) { + t.Errorf("Result contains invalid characters: %q", result) + } + }) + } + }) +} From e9547553abf4beb689ade31cb61be520cb9e100a Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:09:37 -0300 Subject: [PATCH 7/8] ci: created workflow that runs the tests --- .github/workflows/tests.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..95fc643 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,28 @@ +name: Go Test + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.23 + + - name: Install dependencies + run: go mod tidy + + - name: Run tests + run: go test ./... -v From 5a2e08dd22fc26be498d519b2baefe226dcfc611 Mon Sep 17 00:00:00 2001 From: diaszano <61257292+Diaszano@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:07:59 -0300 Subject: [PATCH 8/8] docs: created project documentation --- .github/assets/logo.svg | 87 ++++++++++++++++++++++ README.md | 157 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 .github/assets/logo.svg diff --git a/.github/assets/logo.svg b/.github/assets/logo.svg new file mode 100644 index 0000000..e76f7c5 --- /dev/null +++ b/.github/assets/logo.svg @@ -0,0 +1,87 @@ + + + + + Created by potrace 1.16, written by Peter Selinger 2001-2019 + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index e8d6cfc..630ca7a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,155 @@ -# strrand -A simple and efficient library written in Go (Golang) for generating customizable random strings. +
+
+ + + logo + + +

strrand

+ +

A simple and efficient library written in Go (Golang) for generating customizable random strings.

+ +
+
+ +# Introduction + +The **strrand** library is designed to simplify the generation of random strings quickly, efficiently, and hassle-free. Moreover, it has no external dependencies, ensuring lightweight and easy integration into your Go projects. + +## Installation + +To install the library, simply run the command below: + +```shell +go get github.com/Diaszano/strrand +``` + +## Usage + +Using **strrand** is very intuitive. Check out some practical examples below to get started: + +### Generate Binary Strings +```go +package main + +import ( + "fmt" + + "github.com/Diaszano/strrand" +) + +func main() { + for range 3 { + str := strrand.Binary(10) + fmt.Println(str) + } +} + +// Example output: +// 0111110101 +// 0101111110 +// 0110000001 +``` + +### Generate Hexadecimal Strings +```go +package main + +import ( + "fmt" + + "github.com/Diaszano/strrand" +) + +func main() { + for range 3 { + str := strrand.Hexadecimal(10) + fmt.Println(str) + } +} + +// Example output: +// 8a72bc19a7 +// 652bac2a41 +// 75ff495c77 +``` + +### Generate Base62 Strings +```go +package main + +import ( + "fmt" + + "github.com/Diaszano/strrand" +) + +func main() { + for range 3 { + str := strrand.Base62(10) + fmt.Println(str) + } +} + +// Example output: +// Nwc5Q0ARc4 +// 6D1RLHU4eL +// 8djCoMKfh8 +``` + +### Generate Strings with Random Characters (Default) +```go +package main + +import ( + "fmt" + + "github.com/Diaszano/strrand" +) + +func main() { + for range 3 { + str := strrand.String(10) + fmt.Println(str) + } +} + +// Example output: +// %`'s72,6;3 +// gt)Dn5rNZi +// @AH!=qk^R8 +``` + +### Generate Custom Strings +You can specify a custom character set for string generation: + +```go +package main + +import ( + "fmt" + + "github.com/Diaszano/strrand" +) + +func main() { + charset := "abcd12345" + for range 3 { + str := strrand.String(10, charset) + fmt.Println(str) + } +} + +// Example output: +// 4b42233cc3 +// ba4c41c2c2 +// 345d4acd51 +``` + +## Contribution + +Contributions are welcome! If you find any issues, have suggestions, or want to collaborate with improvements, feel free to open an [issue](https://github.com/Diaszano/strrand/issues) or submit a pull request. + +--- + +**License:** This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).