From c0235a2a356c00ed6e9b561ffa099987d72f5215 Mon Sep 17 00:00:00 2001 From: Naveen Mahalingam Date: Sat, 20 Apr 2024 10:00:34 -0700 Subject: [PATCH] refactor sequencer into sub-package --- Makefile | 2 +- README.md | 55 ++++++++++++---------- {password => charset}/charset.go | 2 +- {password => charset}/charset_test.go | 2 +- main.go | 32 +++++++------ passphrase/rules.go | 46 ++++++------------ password/default_generator.go | 4 +- password/generator.go | 8 +++- password/generator_test.go | 17 +++---- password/rules.go | 51 +++++++------------- password/sequencer/errors.go | 9 ++++ password/sequencer/rules.go | 27 +++++++++++ password/{ => sequencer}/sequencer.go | 13 +++-- password/{ => sequencer}/sequencer_test.go | 37 ++++++++------- password/{ => sequencer}/utils.go | 10 ++-- password/sequencer/utils_test.go | 20 ++++++++ password/utils_test.go | 19 -------- 17 files changed, 190 insertions(+), 164 deletions(-) rename {password => charset}/charset.go (98%) rename {password => charset}/charset_test.go (98%) create mode 100644 password/sequencer/errors.go create mode 100644 password/sequencer/rules.go rename password/{ => sequencer}/sequencer.go (96%) rename password/{ => sequencer}/sequencer_test.go (90%) rename password/{ => sequencer}/utils.go (63%) create mode 100644 password/sequencer/utils_test.go delete mode 100644 password/utils_test.go diff --git a/Makefile b/Makefile index 7a8fab0..3e406d2 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ default: test bench: - go test -bench=. -benchmem ./passphrase ./password + go test -bench=. -benchmem ./passphrase ./password ./password/sequencer cyclo: gocyclo -over 13 ./*/*.go diff --git a/README.md b/README.md index c680ff7..7e96792 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Passphrase & Password generation library for GoLang. ## Passphrases ```golang - generator, err := passphrase.NewGenerator( + g, err := passphrase.NewGenerator( passphrase.WithCapitalizedWords(true), passphrase.WithDictionary(dictionaries.English()), passphrase.WithNumWords(3), @@ -21,7 +21,7 @@ Passphrase & Password generation library for GoLang. panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Passphrase #%3d: %#v\n", idx, generator.Generate()) + fmt.Printf("Passphrase #%3d: %#v\n", idx, g.Generate()) } ```
@@ -42,8 +42,8 @@ Passphrase # 10: "Mirks6-Woofer-Lase" ## Passwords ```golang - generator, err := password.NewGenerator( - password.WithCharset(password.AllChars.WithoutAmbiguity().WithoutDuplicates()), + g, err := password.NewGenerator( + password.WithCharset(charset.AllChars.WithoutAmbiguity().WithoutDuplicates()), password.WithLength(12), password.WithMinLowerCase(5), password.WithMinUpperCase(2), @@ -53,7 +53,7 @@ Passphrase # 10: "Mirks6-Woofer-Lase" panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Password #%3d: %#v\n", idx, generator.Generate()) + fmt.Printf("Password #%3d: %#v\n", idx, g.Generate()) } ```
@@ -75,20 +75,20 @@ Password # 10: "kmQVb&fPqexj" ### Sequential Passwords ```golang - sequencer, err := password.NewSequencer( - password.WithCharset(password.AllChars.WithoutAmbiguity()), - password.WithLength(8), + s, err := sequencer.New( + sequencer.WithCharset(charset.AllChars.WithoutAmbiguity()), + sequencer.WithLength(8), ) if err != nil { panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Password #%3d: %#v\n", idx, sequencer.Get()) + fmt.Printf("Password #%3d: %#v\n", idx, s.Get()) - if !sequencer.HasNext() { + if !s.HasNext() { break } - sequencer.Next() + s.Next() } ```
@@ -109,9 +109,9 @@ Password # 10: "AAAAAAAK" #### Streamed (for async processing) ```golang - sequencer, err := password.NewSequencer( - password.WithCharset(password.Charset("AB")), - password.WithLength(4), + s, err := sequencer.New( + sequencer.WithCharset(charset.Charset("AB")), + sequencer.WithLength(4), ) if err != nil { panic(err.Error()) @@ -122,7 +122,7 @@ Password # 10: "AAAAAAAK" chPasswords := make(chan string, 1) go func() { - err := sequencer.Stream(ctx, chPasswords) + err := s.Stream(ctx, chPasswords) if err != nil { panic(err.Error()) } @@ -170,20 +170,27 @@ goos: linux goarch: amd64 pkg: github.com/jedib0t/go-passwords/passphrase cpu: AMD Ryzen 9 5900X 12-Core Processor -BenchmarkGenerator_Generate-12 2862954 393.5 ns/op 167 B/op 8 allocs/op +BenchmarkGenerator_Generate-12 3015926 392.7 ns/op 167 B/op 8 allocs/op PASS -ok github.com/jedib0t/go-passwords/passphrase 1.567s +ok github.com/jedib0t/go-passwords/passphrase 1.603s goos: linux goarch: amd64 pkg: github.com/jedib0t/go-passwords/password cpu: AMD Ryzen 9 5900X 12-Core Processor -BenchmarkGenerator_Generate-12 6413606 185.3 ns/op 40 B/op 2 allocs/op -BenchmarkSequencer_GotoN-12 4353010 272.5 ns/op 32 B/op 3 allocs/op -BenchmarkSequencer_Next-12 13955396 84.61 ns/op 16 B/op 1 allocs/op -BenchmarkSequencer_NextN-12 6473270 183.9 ns/op 32 B/op 3 allocs/op -BenchmarkSequencer_Prev-12 13106161 87.22 ns/op 16 B/op 1 allocs/op -BenchmarkSequencer_PrevN-12 3967755 288.8 ns/op 32 B/op 3 allocs/op +BenchmarkGenerator_Generate-12 6263398 187.5 ns/op 40 B/op 2 allocs/op PASS -ok github.com/jedib0t/go-passwords/password 8.192s +ok github.com/jedib0t/go-passwords/password 1.375s + +goos: linux +goarch: amd64 +pkg: github.com/jedib0t/go-passwords/password/sequencer +cpu: AMD Ryzen 9 5900X 12-Core Processor +BenchmarkSequencer_GotoN-12 4355002 274.6 ns/op 32 B/op 3 allocs/op +BenchmarkSequencer_Next-12 13614666 88.99 ns/op 16 B/op 1 allocs/op +BenchmarkSequencer_NextN-12 6216072 187.2 ns/op 32 B/op 3 allocs/op +BenchmarkSequencer_Prev-12 13569340 87.69 ns/op 16 B/op 1 allocs/op +BenchmarkSequencer_PrevN-12 4230654 277.9 ns/op 32 B/op 3 allocs/op +PASS +ok github.com/jedib0t/go-passwords/password/sequencer 6.888s ``` diff --git a/password/charset.go b/charset/charset.go similarity index 98% rename from password/charset.go rename to charset/charset.go index 395b3d6..1c594f6 100644 --- a/password/charset.go +++ b/charset/charset.go @@ -1,4 +1,4 @@ -package password +package charset import ( "math/rand" diff --git a/password/charset_test.go b/charset/charset_test.go similarity index 98% rename from password/charset_test.go rename to charset/charset_test.go index 789a8e6..03c16f4 100644 --- a/password/charset_test.go +++ b/charset/charset_test.go @@ -1,4 +1,4 @@ -package password +package charset import ( "math/rand" diff --git a/main.go b/main.go index 71dff92..b536823 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,11 @@ import ( "fmt" "time" + "github.com/jedib0t/go-passwords/charset" "github.com/jedib0t/go-passwords/passphrase" "github.com/jedib0t/go-passwords/passphrase/dictionaries" "github.com/jedib0t/go-passwords/password" + "github.com/jedib0t/go-passwords/password/sequencer" ) func main() { @@ -29,7 +31,7 @@ func main() { } func passphraseGenerator() { - generator, err := passphrase.NewGenerator( + g, err := passphrase.NewGenerator( passphrase.WithCapitalizedWords(true), passphrase.WithDictionary(dictionaries.English()), passphrase.WithNumWords(3), @@ -41,13 +43,13 @@ func passphraseGenerator() { panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Passphrase #%3d: %#v\n", idx, generator.Generate()) + fmt.Printf("Passphrase #%3d: %#v\n", idx, g.Generate()) } } func passwordGenerator() { - generator, err := password.NewGenerator( - password.WithCharset(password.AllChars.WithoutAmbiguity().WithoutDuplicates()), + g, err := password.NewGenerator( + password.WithCharset(charset.AllChars.WithoutAmbiguity().WithoutDuplicates()), password.WithLength(12), password.WithMinLowerCase(5), password.WithMinUpperCase(2), @@ -57,32 +59,32 @@ func passwordGenerator() { panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Password #%3d: %#v\n", idx, generator.Generate()) + fmt.Printf("Password #%3d: %#v\n", idx, g.Generate()) } } func passwordSequencer() { - sequencer, err := password.NewSequencer( - password.WithCharset(password.AllChars.WithoutAmbiguity()), - password.WithLength(8), + s, err := sequencer.New( + sequencer.WithCharset(charset.AllChars.WithoutAmbiguity()), + sequencer.WithLength(8), ) if err != nil { panic(err.Error()) } for idx := 1; idx <= 10; idx++ { - fmt.Printf("Password #%3d: %#v\n", idx, sequencer.Get()) + fmt.Printf("Password #%3d: %#v\n", idx, s.Get()) - if !sequencer.HasNext() { + if !s.HasNext() { break } - sequencer.Next() + s.Next() } } func passwordSequencerStreaming() { - sequencer, err := password.NewSequencer( - password.WithCharset(password.Charset("AB")), - password.WithLength(4), + s, err := sequencer.New( + sequencer.WithCharset(charset.Charset("AB")), + sequencer.WithLength(4), ) if err != nil { panic(err.Error()) @@ -93,7 +95,7 @@ func passwordSequencerStreaming() { chPasswords := make(chan string, 1) go func() { - err := sequencer.Stream(ctx, chPasswords) + err := s.Stream(ctx, chPasswords) if err != nil { panic(err.Error()) } diff --git a/passphrase/rules.go b/passphrase/rules.go index abd34d9..c309f7c 100644 --- a/passphrase/rules.go +++ b/passphrase/rules.go @@ -3,7 +3,7 @@ package passphrase import "github.com/jedib0t/go-passwords/passphrase/dictionaries" // Rule controls how the Generator/Sequencer generates passwords. -type Rule func(a any) +type Rule func(g *generator) var ( basicRules = []Rule{ @@ -18,59 +18,41 @@ var ( // WithCapitalizedWords ensures the words are Capitalized. func WithCapitalizedWords(enabled bool) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.capitalize = enabled - } + return func(g *generator) { + g.capitalize = enabled } } func WithDictionary(words []string) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.dictionary = words - } + return func(g *generator) { + g.dictionary = words } } // WithNumber injects a random number after one of the words in the passphrase. func WithNumber(enabled bool) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.withNumber = enabled - } + return func(g *generator) { + g.withNumber = enabled } } // WithNumWords sets the number of words in the passphrase. func WithNumWords(n int) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.numWords = n - } + return func(g *generator) { + g.numWords = n } } // WithSeparator sets up the delimiter to separate words. func WithSeparator(s string) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.separator = s - } + return func(g *generator) { + g.separator = s } } func WithWordLength(min, max int) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.wordLenMin = min - v.wordLenMax = max - } + return func(g *generator) { + g.wordLenMin = min + g.wordLenMax = max } } diff --git a/password/default_generator.go b/password/default_generator.go index 5541f40..a4cada0 100644 --- a/password/default_generator.go +++ b/password/default_generator.go @@ -1,8 +1,10 @@ package password +import "github.com/jedib0t/go-passwords/charset" + var ( defaultGenerator, _ = NewGenerator( - WithCharset(AllChars.WithoutAmbiguity()), + WithCharset(charset.AllChars.WithoutAmbiguity()), WithLength(12), WithMinLowerCase(3), WithMinUpperCase(1), diff --git a/password/generator.go b/password/generator.go index 6d44070..b7e4dec 100644 --- a/password/generator.go +++ b/password/generator.go @@ -5,6 +5,8 @@ import ( "sync" "time" "unicode" + + "github.com/jedib0t/go-passwords/charset" ) var ( @@ -45,8 +47,8 @@ func NewGenerator(rules ...Rule) (Generator, error) { // split the charsets g.charsetCaseLower = filterRunes(g.charset, unicode.IsLower) g.charsetCaseUpper = filterRunes(g.charset, unicode.IsUpper) - g.charsetNonSymbols = filterRunes(g.charset, func(r rune) bool { return !Symbols.Contains(r) }) - g.charsetSymbols = filterRunes(g.charset, Symbols.Contains) + g.charsetNonSymbols = filterRunes(g.charset, func(r rune) bool { return !charset.Symbols.Contains(r) }) + g.charsetSymbols = filterRunes(g.charset, charset.Symbols.Contains) // create a storage pool with enough objects to support enough parallelism g.pool = &sync.Pool{ @@ -111,6 +113,8 @@ func (g *generator) numSymbolsToGenerate() int { return 0 } +func (g *generator) ruleEnforcer() {} + func (g *generator) sanitize() (Generator, error) { // validate the inputs if len(g.charset) == 0 { diff --git a/password/generator_test.go b/password/generator_test.go index cb2d493..b21ce3e 100644 --- a/password/generator_test.go +++ b/password/generator_test.go @@ -6,12 +6,13 @@ import ( "testing" "unicode" + "github.com/jedib0t/go-passwords/charset" "github.com/stretchr/testify/assert" ) func BenchmarkGenerator_Generate(b *testing.B) { g, err := NewGenerator( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -24,7 +25,7 @@ func BenchmarkGenerator_Generate(b *testing.B) { func TestGenerator_Generate(t *testing.T) { g, err := NewGenerator( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(t, err) @@ -60,7 +61,7 @@ func TestGenerator_Generate(t *testing.T) { func TestGenerator_Generate_WithAMixOfEverything(t *testing.T) { g, err := NewGenerator( - WithCharset(AllChars.WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.AllChars.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), WithMinLowerCase(5), WithMinUpperCase(2), @@ -94,7 +95,7 @@ func TestGenerator_Generate_WithAMixOfEverything(t *testing.T) { assert.True(t, numLowerCase >= 5, password) numUpperCase := len(filterRunes([]rune(password), unicode.IsUpper)) assert.True(t, numUpperCase >= 2, password) - numSymbols := len(filterRunes([]rune(password), Symbols.Contains)) + numSymbols := len(filterRunes([]rune(password), charset.Symbols.Contains)) assert.True(t, numSymbols == 1, password) } if !slices.Equal(expectedPasswords, actualPasswords) { @@ -107,7 +108,7 @@ func TestGenerator_Generate_WithAMixOfEverything(t *testing.T) { func TestGenerator_Generate_WithSymbols(t *testing.T) { t.Run("min 0 max 3", func(t *testing.T) { g, err := NewGenerator( - WithCharset(Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), WithLength(12), WithNumSymbols(0, 3), ) @@ -148,7 +149,7 @@ func TestGenerator_Generate_WithSymbols(t *testing.T) { t.Run("min X max 3", func(t *testing.T) { for _, x := range []int{0, 1, 2, 3} { g, err := NewGenerator( - WithCharset(Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), WithLength(12), WithNumSymbols(x, 3), ) @@ -169,7 +170,7 @@ func TestGenerator_Generate_WithSymbols(t *testing.T) { t.Run("min X max X", func(t *testing.T) { for _, x := range []int{0, 4, 6, 8, 12} { g, err := NewGenerator( - WithCharset(Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), + WithCharset(charset.Charset("abcdef123456-+!@#$%").WithoutAmbiguity().WithoutDuplicates()), WithLength(12), WithNumSymbols(x, x), ) @@ -203,7 +204,7 @@ func TestGenerator_numSymbolsToGenerate(t *testing.T) { func getNumSymbols(pw string) int { rsp := 0 for _, r := range pw { - if Symbols.Contains(r) { + if charset.Symbols.Contains(r) { rsp++ } } diff --git a/password/rules.go b/password/rules.go index eafa622..846b488 100644 --- a/password/rules.go +++ b/password/rules.go @@ -1,36 +1,28 @@ package password +import "github.com/jedib0t/go-passwords/charset" + // Rule controls how the Generator/Sequencer generates passwords. -type Rule func(any) +type Rule func(g *generator) var ( basicRules = []Rule{ - WithCharset(AlphaNumeric), - WithLength(8), + WithCharset(charset.AllChars), + WithLength(12), } ) // WithCharset sets the Charset the Generator/Sequencer can use. -func WithCharset(c Charset) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.charset = []rune(c) - case *sequencer: - v.charset = []rune(c) - } +func WithCharset(c charset.Charset) Rule { + return func(g *generator) { + g.charset = []rune(c) } } // WithLength sets the length of the generated password. func WithLength(l int) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.numChars = l - case *sequencer: - v.numChars = l - } + return func(g *generator) { + g.numChars = l } } @@ -39,11 +31,8 @@ func WithLength(l int) Rule { // // Note: This works only on a Generator and is ineffective with a Sequencer. func WithMinLowerCase(min int) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.minLowerCase = min - } + return func(g *generator) { + g.minLowerCase = min } } @@ -52,11 +41,8 @@ func WithMinLowerCase(min int) Rule { // // Note: This works only on a Generator and is ineffective with a Sequencer. func WithMinUpperCase(min int) Rule { - return func(a any) { - switch v := a.(type) { - case *generator: - v.minUpperCase = min - } + return func(g *generator) { + g.minUpperCase = min } } @@ -76,11 +62,8 @@ func WithNumSymbols(min, max int) Rule { min = max } - return func(a any) { - switch v := a.(type) { - case *generator: - v.minSymbols = min - v.maxSymbols = max - } + return func(g *generator) { + g.minSymbols = min + g.maxSymbols = max } } diff --git a/password/sequencer/errors.go b/password/sequencer/errors.go new file mode 100644 index 0000000..5164658 --- /dev/null +++ b/password/sequencer/errors.go @@ -0,0 +1,9 @@ +package sequencer + +import "errors" + +var ( + ErrEmptyCharset = errors.New("cannot generate passwords with empty charset") + ErrInvalidN = errors.New("value of N exceeds valid range") + ErrZeroLenPassword = errors.New("cannot generate passwords with 0 length") +) diff --git a/password/sequencer/rules.go b/password/sequencer/rules.go new file mode 100644 index 0000000..82070b3 --- /dev/null +++ b/password/sequencer/rules.go @@ -0,0 +1,27 @@ +package sequencer + +import "github.com/jedib0t/go-passwords/charset" + +// Rule controls how the Generator/Sequencer generates passwords. +type Rule func(s *sequencer) + +var ( + basicRules = []Rule{ + WithCharset(charset.AlphaNumeric), + WithLength(12), + } +) + +// WithCharset sets the Charset the Generator/Sequencer can use. +func WithCharset(c charset.Charset) Rule { + return func(s *sequencer) { + s.charset = []rune(c) + } +} + +// WithLength sets the length of the generated password. +func WithLength(l int) Rule { + return func(s *sequencer) { + s.numChars = l + } +} diff --git a/password/sequencer.go b/password/sequencer/sequencer.go similarity index 96% rename from password/sequencer.go rename to password/sequencer/sequencer.go index 7048319..e9ece08 100644 --- a/password/sequencer.go +++ b/password/sequencer/sequencer.go @@ -1,4 +1,4 @@ -package password +package sequencer import ( "context" @@ -6,6 +6,8 @@ import ( "math/big" "math/rand/v2" "sync" + + "github.com/jedib0t/go-passwords/charset" ) var ( @@ -62,9 +64,8 @@ type sequencer struct { rng *rand.Rand } -// NewSequencer returns a password sequencer that implements the Sequencer -// interface. -func NewSequencer(rules ...Rule) (Sequencer, error) { +// New returns a password Sequencer. +func New(rules ...Rule) (Sequencer, error) { s := &sequencer{} for _, rule := range append(basicRules, rules...) { rule(s) @@ -74,7 +75,7 @@ func NewSequencer(rules ...Rule) (Sequencer, error) { s.base = big.NewInt(int64(len(s.charset))) s.charsetLen = len(s.charset) s.charsetMaxIdx = len(s.charset) - 1 - s.maxWords = MaximumPossibleWords(Charset(s.charset), s.numChars) + s.maxWords = MaximumPossibleWords(charset.Charset(s.charset), s.numChars) s.n = big.NewInt(0) s.nMax = new(big.Int).Sub(s.maxWords, biOne) s.password = make([]int, s.numChars) @@ -308,3 +309,5 @@ func (s *sequencer) prevAtIndex(idx int) bool { } return false } + +func (s *sequencer) ruleEnforcer() {} diff --git a/password/sequencer_test.go b/password/sequencer/sequencer_test.go similarity index 90% rename from password/sequencer_test.go rename to password/sequencer/sequencer_test.go index e1bf1c1..c3b42e7 100644 --- a/password/sequencer_test.go +++ b/password/sequencer/sequencer_test.go @@ -1,4 +1,4 @@ -package password +package sequencer import ( "context" @@ -9,12 +9,13 @@ import ( "testing" "time" + "github.com/jedib0t/go-passwords/charset" "github.com/stretchr/testify/assert" ) func BenchmarkSequencer_GotoN(b *testing.B) { - s, err := NewSequencer( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + s, err := New( + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -31,8 +32,8 @@ func BenchmarkSequencer_GotoN(b *testing.B) { } func BenchmarkSequencer_Next(b *testing.B) { - s, err := NewSequencer( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + s, err := New( + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -45,8 +46,8 @@ func BenchmarkSequencer_Next(b *testing.B) { } func BenchmarkSequencer_NextN(b *testing.B) { - s, err := NewSequencer( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + s, err := New( + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -60,8 +61,8 @@ func BenchmarkSequencer_NextN(b *testing.B) { } func BenchmarkSequencer_Prev(b *testing.B) { - s, err := NewSequencer( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + s, err := New( + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -74,8 +75,8 @@ func BenchmarkSequencer_Prev(b *testing.B) { } func BenchmarkSequencer_PrevN(b *testing.B) { - s, err := NewSequencer( - WithCharset(AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), + s, err := New( + WithCharset(charset.AlphaNumeric.WithoutAmbiguity().WithoutDuplicates()), WithLength(12), ) assert.Nil(b, err) @@ -89,14 +90,14 @@ func BenchmarkSequencer_PrevN(b *testing.B) { } func TestSequencer(t *testing.T) { - s, err := NewSequencer( + s, err := New( WithCharset(""), WithLength(3), ) assert.Nil(t, s) assert.NotNil(t, err) assert.True(t, errors.Is(err, ErrEmptyCharset)) - s, err = NewSequencer( + s, err = New( WithCharset("AB"), WithLength(0), ) @@ -104,7 +105,7 @@ func TestSequencer(t *testing.T) { assert.NotNil(t, err) assert.True(t, errors.Is(err, ErrZeroLenPassword)) - s, err = NewSequencer( + s, err = New( WithCharset("AB"), WithLength(3), ) @@ -171,7 +172,7 @@ func TestSequencer(t *testing.T) { } func TestSequencer_GotoN(t *testing.T) { - s, err := NewSequencer( + s, err := New( WithCharset("AB"), WithLength(3), ) @@ -184,7 +185,7 @@ func TestSequencer_GotoN(t *testing.T) { assert.NotNil(t, err) assert.True(t, errors.Is(err, ErrInvalidN)) - s, err = NewSequencer( + s, err = New( WithCharset("AB"), WithLength(4), ) @@ -215,7 +216,7 @@ func TestSequencer_GotoN(t *testing.T) { } func TestSequencer_Stream(t *testing.T) { - s, err := NewSequencer( + s, err := New( WithCharset("AB"), WithLength(3), ) @@ -266,7 +267,7 @@ func TestSequencer_Stream(t *testing.T) { } func TestSequencer_Stream_Limited(t *testing.T) { - s, err := NewSequencer( + s, err := New( WithCharset("AB"), WithLength(3), ) diff --git a/password/utils.go b/password/sequencer/utils.go similarity index 63% rename from password/utils.go rename to password/sequencer/utils.go index ad878c3..87d9fc1 100644 --- a/password/utils.go +++ b/password/sequencer/utils.go @@ -1,11 +1,15 @@ -package password +package sequencer -import "math/big" +import ( + "math/big" + + "github.com/jedib0t/go-passwords/charset" +) // MaximumPossibleWords returns the maximum number of unique passwords that can // be generated with the given Charset and the number of characters allowed in // the password. -func MaximumPossibleWords(charset Charset, numChars int) *big.Int { +func MaximumPossibleWords(charset charset.Charset, numChars int) *big.Int { i, e := big.NewInt(int64(len(charset))), big.NewInt(int64(numChars)) return i.Exp(i, e, nil) } diff --git a/password/sequencer/utils_test.go b/password/sequencer/utils_test.go new file mode 100644 index 0000000..894765a --- /dev/null +++ b/password/sequencer/utils_test.go @@ -0,0 +1,20 @@ +package sequencer + +import ( + "testing" + + "github.com/jedib0t/go-passwords/charset" + "github.com/stretchr/testify/assert" +) + +func TestMaximumPossibleWords(t *testing.T) { + assert.Equal(t, "10", MaximumPossibleWords(charset.Numbers, 1).String()) + assert.Equal(t, "10000", MaximumPossibleWords(charset.Numbers, 4).String()) + assert.Equal(t, "100000000", MaximumPossibleWords(charset.Numbers, 8).String()) + assert.Equal(t, "16777216", MaximumPossibleWords(charset.Symbols, 8).String()) + assert.Equal(t, "377801998336", MaximumPossibleWords(charset.SymbolsFull, 8).String()) + assert.Equal(t, "53459728531456", MaximumPossibleWords(charset.Alphabets, 8).String()) + assert.Equal(t, "218340105584896", MaximumPossibleWords(charset.AlphaNumeric, 8).String()) + assert.Equal(t, "576480100000000", MaximumPossibleWords(charset.AllChars, 8).String()) + assert.Equal(t, "4304672100000000", MaximumPossibleWords(charset.AlphaNumeric+charset.SymbolsFull, 8).String()) +} diff --git a/password/utils_test.go b/password/utils_test.go deleted file mode 100644 index 478c38f..0000000 --- a/password/utils_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package password - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMaximumPossibleWords(t *testing.T) { - assert.Equal(t, "10", MaximumPossibleWords(Numbers, 1).String()) - assert.Equal(t, "10000", MaximumPossibleWords(Numbers, 4).String()) - assert.Equal(t, "100000000", MaximumPossibleWords(Numbers, 8).String()) - assert.Equal(t, "16777216", MaximumPossibleWords(Symbols, 8).String()) - assert.Equal(t, "377801998336", MaximumPossibleWords(SymbolsFull, 8).String()) - assert.Equal(t, "53459728531456", MaximumPossibleWords(Alphabets, 8).String()) - assert.Equal(t, "218340105584896", MaximumPossibleWords(AlphaNumeric, 8).String()) - assert.Equal(t, "576480100000000", MaximumPossibleWords(AllChars, 8).String()) - assert.Equal(t, "4304672100000000", MaximumPossibleWords(AlphaNumeric+SymbolsFull, 8).String()) -}