Skip to content

Commit

Permalink
Merge pull request #4 from dominikwinter/3-add-flags-to-make-the-app-…
Browse files Browse the repository at this point in the history
…flexible

add the possibility to change the behavior via flags to make the app …
  • Loading branch information
dominikwinter authored Jan 26, 2024
2 parents 3a8cf13 + e8082e5 commit 088c81d
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 130 deletions.
31 changes: 17 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
.PHONY: default t test b build i install c clean r release
.PHONY: default test build install clean release

default: build

t test:
go test -v
run:
go run cmd/genpno/main.go

b build:
go build
test:
go test -v --race -cpu 2 -cover -shuffle on -vet '' ./...

i install:
go install
build:
go build -o bin/genpno cmd/genpno/main.go

c clean:
install:
go install bin/genpno

clean:
go mod tidy

r release:
release:
mkdir -p bin
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o bin/genpno-macos-arm64
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/genpno-linux-amd64
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o bin/genpno-linux-386
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/genpno-windows-amd64.exe
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o bin/genpno-windows-386.exe
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o bin/genpno-macos-arm64 cmd/genpno/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/genpno-linux-amd64 cmd/genpno/main.go
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o bin/genpno-linux-386 cmd/genpno/main.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/genpno-windows-amd64.exe cmd/genpno/main.go
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o bin/genpno-windows-386.exe cmd/genpno/main.go
42 changes: 42 additions & 0 deletions cmd/genpno/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"flag"
"fmt"

"github.com/dominikwinter/genpno/internal/generator"
)

func main() {
helpFlag := flag.Bool("h", false, "Help")
verboseFlag := flag.Bool("v", false, "Verbose")

dateFlag := flag.String("d", "", "Date (format: yyyymmdd) (default random)")
countryFlag := flag.String("c", "", "Country code (default random)")
genderFlag := flag.String("g", "", "Gender (m/f) (default random)")
formatFlag := flag.String("f", "", `Output forma (default 3):
1. yymmddccgn
2. yymmdd-ccgn
3. yyyymmddccgn
4. yyyymmdd-ccgn`)

flag.Parse()

if *helpFlag {
flag.Usage()
return
}

pno, err := generator.GeneratePno(*dateFlag, *countryFlag, *genderFlag, *formatFlag)

if err != nil {
if *verboseFlag {
panic(err)
} else {
fmt.Println(err)
return
}
}

fmt.Println(pno)
}
39 changes: 39 additions & 0 deletions internal/generator/checksum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package generator

import (
"fmt"
"strconv"
)

func GetShortDate(date string) string {
return date[2:]
}

func GetChecksum(date, country, gender string) (string, error) {
if l := len(date); l != 8 {
return "", fmt.Errorf("date is not 8 characters long, but '%d'", l)
}

shortDate := GetShortDate(date)
num := shortDate + country + gender
numLen := len(num)

var checksum int

for i := numLen - 2; i >= 0; i -= 2 {
n := num[i] - '0'
checksum += int(n)
}

for i := numLen - 1; i >= 0; i -= 2 {
n := (num[i] - '0') * 2

if n > 9 {
n -= 9
}

checksum += int(n)
}

return strconv.Itoa(checksum * 9 % 10), nil
}
26 changes: 26 additions & 0 deletions internal/generator/country.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package generator

import (
"fmt"
"math/rand"
"strconv"
)

func GetCountry(country string) (string, error) {
if l := len(country); l != 2 {
return "", fmt.Errorf("country is not 2 characters long, but '%d'", l)
}

c, err := strconv.Atoi(country)
if err != nil {
return "", fmt.Errorf("could not parse country '%s': %w", country, err)
}

return fmt.Sprintf("%02d", c), nil
}

func GetRandomCountry() (string, error) {
country := rand.Intn(99)

return fmt.Sprintf("%02d", country), nil
}
32 changes: 32 additions & 0 deletions internal/generator/date.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package generator

import (
"fmt"
"math/rand"
"time"
)

const DATE_FORMAT = "20060102"

func GetRandomDate() (string, error) {
now := time.Now()

min := now.AddDate(-100, 0, 0).Unix()
max := now.AddDate(-18, 0, 0).Unix()

delta := max - min
sec := rand.Int63n(delta) + min

return time.Unix(sec, 0).Format(DATE_FORMAT), nil
}

func GetDate(date string) (string, error) {
d, err := time.Parse(DATE_FORMAT, date)
if err != nil {
return "", fmt.Errorf("could not parse date: '%s': %w", date, err)
}

sec := d.Unix()

return time.Unix(sec, 0).Format(DATE_FORMAT), nil
}
22 changes: 22 additions & 0 deletions internal/generator/format.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package generator

import "fmt"

func GetFormat(formatFlag, date, country, gender, checksum string) (string, error) {
switch formatFlag {
case "1", "yymmddccgn":
return GetShortDate(date) + country + gender + checksum, nil

case "2", "yymmdd-ccgn":
return GetShortDate(date) + "-" + country + gender + checksum, nil

case "3", "yyyymmddccgn", "":
return date + country + gender + checksum, nil

case "4", "yyyymmdd-ccgn":
return date + "-" + country + gender + checksum, nil

default:
return "", fmt.Errorf("unknown format '%s'", formatFlag)
}
}
35 changes: 35 additions & 0 deletions internal/generator/gender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package generator

import (
"fmt"
"math/rand"
"strconv"
)

func GetGenderNumber(gender string) (string, error) {
var num int

switch gender {
case "m":
// generate random single digit odd number
num = rand.Intn(10) | 1 // force last bit to 1

case "f":
// generate random single digit even number
num = rand.Intn(8)
num += num & 1 // force last bit to 0

default:
return "", fmt.Errorf("unknown gender '%s'", gender)
}

return strconv.Itoa(num), nil
}

func GetRandomGenderNumber() (string, error) {
if n := rand.Intn(2); n == 0 {
return GetGenderNumber("m")
} else {
return GetGenderNumber("f")
}
}
39 changes: 39 additions & 0 deletions internal/generator/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package generator

import "fmt"

func GeneratePno(dateFlag, countryFlag, genderFlag, formatFlag string) (string, error) {
date, err := GenerateStr(dateFlag, GetDate, GetRandomDate)
if err != nil {
return "", fmt.Errorf("failed to get date: %w", err)
}

country, err := GenerateStr(countryFlag, GetCountry, GetRandomCountry)
if err != nil {
return "", fmt.Errorf("failed to get country: %w", err)
}

gender, err := GenerateStr(genderFlag, GetGenderNumber, GetRandomGenderNumber)
if err != nil {
return "", fmt.Errorf("failed to get gender: %w", err)
}

checksum, err := GetChecksum(date, country, gender)
if err != nil {
return "", fmt.Errorf("failed to get checksum: %w", err)
}

return GetFormat(formatFlag, date, country, gender, checksum)
}

func GenerateStr[I string, O string](
flag I,
getFunc func(I) (O, error),
getRandomFunc func() (O, error),
) (O, error) {
if len(flag) > 0 {
return getFunc(flag)
}

return getRandomFunc()
}
98 changes: 98 additions & 0 deletions internal/generator/generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package generator

import (
"strconv"
"testing"
)

func Test_GetRandomDate(t *testing.T) {
for i := 0; i < 1000; i++ {
date, err := GetRandomDate()
if err != nil {
t.Error(err)
}

if l := len(date); l != 8 {
t.Errorf("Date is not 8 characters long, but %d", l)
}
}
}

func Test_getRandomBirthCountry(t *testing.T) {
for i := 0; i < 1000; i++ {
country, err := GetRandomCountry()
if err != nil {
t.Error(err)
}

if l := len(country); l != 2 {
t.Errorf("Country is not 2 characters long, but %d", l)
}
}
}

func Test_getRandomGenderNumber(t *testing.T) {
for i := 0; i < 1000; i++ {
gender, err := GetRandomGenderNumber()
if err != nil {
t.Error(err)
}

if l := len(gender); l != 1 {
t.Errorf("Gender is not 1 characters long, but %d", l)
}
}
}

func Test_GetChecksum(t *testing.T) {
date := "19800101"
country := "12"
gender := "1"

checksum, err := GetChecksum(date, country, gender)
if err != nil {
t.Error(err)
}

if l := len(checksum); l != 1 {
t.Errorf("Checksum is not 1 characters long, but %d", l)
}
}

func Test_GetGenderNumber(t *testing.T) {
t.Run("male", func(t *testing.T) {
for i := 0; i < 1000; i++ {
str, err := GetGenderNumber("m")
if err != nil {
t.Error(err)
}

num, err := strconv.Atoi(str)
if err != nil {
t.Error(err)
}

if num%2 == 0 {
t.Errorf("%d is not a male random number", num)
}
}
})

t.Run("female", func(t *testing.T) {
for i := 0; i < 1000; i++ {
str, err := GetGenderNumber("f")
if err != nil {
t.Error(err)
}

num, err := strconv.Atoi(str)
if err != nil {
t.Error(err)
}

if num%2 != 0 {
t.Errorf("%d is not a female random number", num)
}
}
})
}
Loading

0 comments on commit 088c81d

Please sign in to comment.