Add phonetic spelling functionality to password generator

The update introduces a new 'SpellPassword' setting in the configuration that, when enabled, spells out the generated passwords in the phonetic alphabet. The accompanied changes include the addition of 'spelling.go' and 'spelling_test.go' files containing the spelling logic and corresponding tests. The domain-specific error handling is also enhanced for unsupported characters.
This commit is contained in:
Winni Neessen 2024-03-08 16:03:02 +01:00
parent 4219a27007
commit 93f092e690
Signed by: wneessen
GPG key ID: 385AC9889632126E
3 changed files with 168 additions and 0 deletions

View file

@ -41,6 +41,8 @@ type Config struct {
// NumberPass sets the number of passwords that are generated // NumberPass sets the number of passwords that are generated
// and returned by the generator // and returned by the generator
NumberPass int64 NumberPass int64
// SpellPassword if set will spell the generated passwords in the phonetic alphabet
SpellPassword bool
} }
// Option is a function that can override default Config settings // Option is a function that can override default Config settings

113
spelling.go Normal file
View file

@ -0,0 +1,113 @@
package apg
import (
"fmt"
"strings"
)
var (
symbNumNames = map[byte]string{
'1': "ONE",
'2': "TWO",
'3': "THREE",
'4': "FOUR",
'5': "FIVE",
'6': "SIX",
'7': "SEVEN",
'8': "EIGHT",
'9': "NINE",
'0': "ZERO",
33: "EXCLAMATION_POINT",
34: "QUOTATION_MARK",
35: "CROSSHATCH",
36: "DOLLAR_SIGN",
37: "PERCENT_SIGN",
38: "AMPERSAND",
39: "APOSTROPHE",
40: "LEFT_PARENTHESIS",
41: "RIGHT_PARENTHESIS",
42: "ASTERISK",
43: "PLUS_SIGN",
44: "COMMA",
45: "HYPHEN",
46: "PERIOD",
47: "SLASH",
58: "COLON",
59: "SEMICOLON",
60: "LESS_THAN",
61: "EQUAL_SIGN",
62: "GREATER_THAN",
63: "QUESTION_MARK",
64: "AT_SIGN",
91: "LEFT_BRACKET",
92: "BACKSLASH",
93: "RIGHT_BRACKET",
94: "CIRCUMFLEX",
95: "UNDERSCORE",
96: "GRAVE",
123: "LEFT_BRACE",
124: "VERTICAL_BAR",
125: "RIGHT_BRACE",
126: "TILDE",
}
alphabetNames = map[byte]string{
'A': "Alfa",
'B': "Bravo",
'C': "Charlie",
'D': "Delta",
'E': "Echo",
'F': "Foxtrot",
'G': "Golf",
'H': "Hotel",
'I': "India",
'J': "Juliett",
'K': "Kilo",
'L': "Lima",
'M': "Mike",
'N': "November",
'O': "Oscar",
'P': "Papa",
'Q': "Quebec",
'R': "Romeo",
'S': "Sierra",
'T': "Tango",
'U': "Uniform",
'V': "Victor",
'W': "Whiskey",
'X': "X_ray",
'Y': "Yankee",
'Z': "Zulu",
}
)
// Spell returns a given string as spelled english phonetic alphabet string
func Spell(input string) (string, error) {
var returnString []string
for _, curChar := range input {
curSpellString, err := ConvertByteToWord(byte(curChar))
if err != nil {
return "", err
}
returnString = append(returnString, curSpellString)
}
return strings.Join(returnString, "/"), nil
}
// ConvertByteToWord converts a given ASCII byte into the corresponding spelled version
// of the english phonetic alphabet
func ConvertByteToWord(charByte byte) (string, error) {
var returnString string
switch {
case charByte > 64 && charByte < 91:
returnString = alphabetNames[charByte]
case charByte > 96 && charByte < 123:
returnString = strings.ToLower(alphabetNames[charByte-32])
default:
returnString = symbNumNames[charByte]
}
if returnString == "" {
return "", fmt.Errorf("failed to convert given byte to word: %s is unsupported", string(charByte))
}
return returnString, nil
}

53
spelling_test.go Normal file
View file

@ -0,0 +1,53 @@
package apg
import (
"strings"
"testing"
)
func TestConvertByteToWord(t *testing.T) {
tests := []struct {
name string
char byte
want string
wantErr bool
}{
{
name: "UpperCaseChar",
char: 'A',
want: alphabetNames['A'],
wantErr: false,
},
{
name: "LowerCaseChar",
char: 'a',
want: strings.ToLower(alphabetNames['A']),
wantErr: false,
},
{
name: "NonAlphaChar",
char: '(',
want: symbNumNames['('],
wantErr: false,
},
{
name: "UnsupportedChar",
char: 'ü',
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ConvertByteToWord(tt.char)
if (err != nil) != tt.wantErr {
t.Errorf("ConvertByteToWord() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ConvertByteToWord() got = %v, want %v", got, tt.want)
}
})
}
}