#53 Refactor mode handling and bitmask functions

The naming and handling of mode bitmasks have been refactored for improved code readability and maintainability. The term "Mode" was replaced with "ModeMask" for clarity and all associated functions were renamed accordingly (e.g., "SetMode" to "MaskSetMode"). These changes provide better insight into the function of the code and increase understandability for future development efforts. The command-line utility now also supports specifying modes via the "-M" flag.
This commit is contained in:
Winni Neessen 2023-08-04 17:14:24 +02:00
parent ac97b94ec9
commit b31219046a
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
4 changed files with 88 additions and 21 deletions

View file

@ -4,6 +4,7 @@ package main
import (
"flag"
"fmt"
"os"
"github.com/wneessen/apg-go"
@ -13,12 +14,20 @@ func main() {
c := apg.NewConfig()
// Configure and parse the CLI flags
var ms string
flag.Int64Var(&c.MinLength, "m", c.MinLength, "")
flag.Int64Var(&c.MaxLength, "x", c.MaxLength, "")
flag.StringVar(&ms, "M", "", "")
flag.Int64Var(&c.NumberPass, "n", c.NumberPass, "")
flag.Usage = usage
flag.Parse()
if ms != "" {
c.Modes = apg.ModesFromFlags(ms)
}
for _, m := range []apg.Mode{apg.ModeHumanReadable, apg.ModeLowerCase, apg.ModeNumber, apg.ModeSpecial, apg.ModeUpperCase} {
fmt.Printf("%s: %t\n", m, apg.MaskHasMode(c.Modes, m))
}
/*
g := apg.New(c)
rb, err := g.RandomBytes(c.MinLength)

View file

@ -18,6 +18,8 @@ type Config struct {
MaxLength int64
// MinLength sets the minimum length for a generated password
MinLength int64
// Modes holds the different character modes for the Random algorithm
Modes ModeMask
// NumberPass sets the number of passwords that are generated
// and returned by the generator
NumberPass int64

72
mode.go
View file

@ -1,8 +1,13 @@
package apg
import "strings"
// Mode represents a mode of characters
type Mode uint8
// ModeMask represents a bitmask of character modes
type ModeMask uint8
const (
// ModeNumber sets the bitmask to include numbers in the generated passwords
ModeNumber = 1 << iota
@ -38,14 +43,65 @@ const (
CharRangeSpecialHuman = `#%*+-:;=`
)
// SetMode sets a specific Mode to a given Mode bitmask
func SetMode(b, m Mode) Mode { return b | m }
// MaskSetMode sets a specific Mode to a given Mode bitmask
func MaskSetMode(ma ModeMask, mo Mode) ModeMask { return ModeMask(uint8(ma) | uint8(mo)) }
// ClearMode clears a specific Mode from a given Mode bitmask
func ClearMode(b, m Mode) Mode { return b &^ m }
// MaskClearMode clears a specific Mode from a given Mode bitmask
func MaskClearMode(ma ModeMask, mo Mode) ModeMask { return ModeMask(uint8(ma) &^ uint8(mo)) }
// ToggleMode toggles a specific Mode in a given Mode bitmask
func ToggleMode(b, m Mode) Mode { return b ^ m }
// MaskToggleMode toggles a specific Mode in a given Mode bitmask
func MaskToggleMode(ma ModeMask, mo Mode) ModeMask { return ModeMask(uint8(ma) ^ uint8(mo)) }
// HasMode returns true if a given Mode bitmask holds a specific Mode
func HasMode(b, m Mode) bool { return b&m != 0 }
// MaskHasMode returns true if a given Mode bitmask holds a specific Mode
func MaskHasMode(ma ModeMask, mo Mode) bool { return uint8(ma)&uint8(mo) != 0 }
func ModesFromFlags(ms string) ModeMask {
var mm ModeMask
for _, m := range strings.Split(ms, "") {
switch m {
case "C":
mm = MaskSetMode(mm, ModeLowerCase|ModeNumber|ModeSpecial|ModeUpperCase)
case "h":
mm = MaskClearMode(mm, ModeHumanReadable)
case "H":
mm = MaskSetMode(mm, ModeHumanReadable)
case "l":
mm = MaskClearMode(mm, ModeLowerCase)
case "L":
mm = MaskSetMode(mm, ModeLowerCase)
case "n":
mm = MaskClearMode(mm, ModeNumber)
case "N":
mm = MaskSetMode(mm, ModeNumber)
case "s":
mm = MaskClearMode(mm, ModeSpecial)
case "S":
mm = MaskSetMode(mm, ModeSpecial)
case "u":
mm = MaskClearMode(mm, ModeUpperCase)
case "U":
mm = MaskSetMode(mm, ModeUpperCase)
}
}
return mm
}
// String satisfies the fmt.Stringer interface for the Mode type
func (m Mode) String() string {
switch m {
case ModeHumanReadable:
return "Human-readable"
case ModeLowerCase:
return "Lower-case"
case ModeNumber:
return "Number"
case ModeSpecial:
return "Special"
case ModeUpperCase:
return "Upper-case"
default:
return "Unknown"
}
}

View file

@ -18,22 +18,22 @@ func TestSetClearHasToggleMode(t *testing.T) {
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
var m Mode
m = SetMode(m, tc.mode)
if !HasMode(m, tc.mode) {
t.Errorf("SetMode() failed, mode not found in bitmask")
var m ModeMask
m = MaskSetMode(m, tc.mode)
if !MaskHasMode(m, tc.mode) {
t.Errorf("MaskSetMode() failed, mode not found in bitmask")
}
m = ToggleMode(m, tc.mode)
if HasMode(m, tc.mode) {
t.Errorf("ToggleMode() failed, mode found in bitmask")
m = MaskToggleMode(m, tc.mode)
if MaskHasMode(m, tc.mode) {
t.Errorf("MaskToggleMode() failed, mode found in bitmask")
}
m = ToggleMode(m, tc.mode)
if !HasMode(m, tc.mode) {
t.Errorf("ToggleMode() failed, mode not found in bitmask")
m = MaskToggleMode(m, tc.mode)
if !MaskHasMode(m, tc.mode) {
t.Errorf("MaskToggleMode() failed, mode not found in bitmask")
}
m = ClearMode(m, tc.mode)
if HasMode(m, tc.mode) {
t.Errorf("ClearMode() failed, mode found in bitmask")
m = MaskClearMode(m, tc.mode)
if MaskHasMode(m, tc.mode) {
t.Errorf("MaskClearMode() failed, mode found in bitmask")
}
})
}