66 lines
1.7 KiB
Go
66 lines
1.7 KiB
Go
package local
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"galaxy/user/internal/domain/account"
|
|
"galaxy/user/internal/domain/common"
|
|
"galaxy/user/internal/ports"
|
|
|
|
confusables "github.com/disciplinedware/go-confusables"
|
|
"golang.org/x/text/cases"
|
|
)
|
|
|
|
type confusableSkeletoner interface {
|
|
Skeleton(string) string
|
|
}
|
|
|
|
type raceNamePolicy struct {
|
|
caseFolder cases.Caser
|
|
skeletoner confusableSkeletoner
|
|
}
|
|
|
|
var raceNameAntiFraudReplacer = strings.NewReplacer(
|
|
"1", "i",
|
|
"0", "o",
|
|
"8", "b",
|
|
)
|
|
|
|
// NewRaceNamePolicy returns the local Stage 06 race-name canonicalization
|
|
// policy backed by Unicode case folding, explicit ASCII anti-fraud mappings,
|
|
// and a TR39 confusable skeleton.
|
|
func NewRaceNamePolicy() (ports.RaceNamePolicy, error) {
|
|
policy := &raceNamePolicy{
|
|
caseFolder: cases.Fold(),
|
|
skeletoner: confusables.Default(),
|
|
}
|
|
if policy.skeletoner == nil {
|
|
return nil, fmt.Errorf("new race-name policy: nil confusable skeletoner")
|
|
}
|
|
|
|
return policy, nil
|
|
}
|
|
|
|
// CanonicalKey returns the stable uniqueness key for raceName.
|
|
func (policy *raceNamePolicy) CanonicalKey(raceName common.RaceName) (account.RaceNameCanonicalKey, error) {
|
|
switch {
|
|
case policy == nil:
|
|
return "", fmt.Errorf("canonicalize race name: nil policy")
|
|
case policy.skeletoner == nil:
|
|
return "", fmt.Errorf("canonicalize race name: nil confusable skeletoner")
|
|
}
|
|
if err := raceName.Validate(); err != nil {
|
|
return "", fmt.Errorf("canonicalize race name: %w", err)
|
|
}
|
|
|
|
folded := policy.caseFolder.String(raceName.String())
|
|
antiFraudMapped := raceNameAntiFraudReplacer.Replace(folded)
|
|
key := account.RaceNameCanonicalKey(policy.skeletoner.Skeleton(antiFraudMapped))
|
|
if err := key.Validate(); err != nil {
|
|
return "", fmt.Errorf("canonicalize race name: %w", err)
|
|
}
|
|
|
|
return key, nil
|
|
}
|