@@ -10,40 +10,56 @@ import (
1010 "crypto/sha256"
1111
1212 "github.com/aohorodnyk/uid"
13+ "github.com/dustinkirkland/golang-petname"
1314)
1415
15- type Identifier string
16+ type Identifier struct {
17+ name string
18+ salt string
19+ }
1620
1721var ErrWrongIdentifier = errors .New ("wrong identifier" )
1822
1923const (
2024 maximumLongLength = 50
2125 shortLength = 16
26+ nameLength = maximumLongLength - shortLength - uint32 (len ("tpi---" ))
2227)
2328
2429func ParseIdentifier (identifier string ) (Identifier , error ) {
2530 re := regexp .MustCompile (`(?s)^tpi-([a-z0-9]+(?:[a-z0-9-]*[a-z0-9])?)-([a-z0-9]+)-([a-z0-9]+)$` )
2631
2732 if match := re .FindStringSubmatch (string (identifier )); len (match ) > 0 && hash (match [1 ]+ match [2 ], shortLength / 2 ) == match [3 ] {
28- return Identifier ( match [1 ]) , nil
33+ return Identifier { name : match [1 ], salt : match [ 2 ]} , nil
2934 }
3035
31- return Identifier ( "" ) , ErrWrongIdentifier
36+ return Identifier {} , ErrWrongIdentifier
3237}
3338
34- func NewIdentifier (identifier string ) Identifier {
35- return Identifier (identifier )
39+ // NewDeterministicIdentifier returns a new deterministic Identifier, using the
40+ // provided name as a seed. Repeated calls to this function are guaranteed to
41+ // generate the same Identifier.
42+ func NewDeterministicIdentifier (name string ) Identifier {
43+ seed := normalize (name , nameLength )
44+ return Identifier {name : name , salt : hash (seed , shortLength / 2 )}
3645}
3746
38- func NewRandomIdentifier () Identifier {
39- return NewIdentifier (uid .NewProvider36Size (8 ).MustGenerate ().String ())
47+ // NewRandomIdentifier returns a new random Identifier. Repeated calls to this
48+ // function are guaranteed to generate different Identifiers, as long as there
49+ // are no collisions.
50+ func NewRandomIdentifier (name string ) Identifier {
51+ seed := uid .NewProvider36Size (8 ).MustGenerate ().String ()
52+ if name == "" {
53+ petname .NonDeterministicMode ()
54+ name = petname .Generate (3 , "-" )
55+ }
56+
57+ return Identifier {name : name , salt : hash (seed , shortLength / 2 )}
4058}
4159
4260func (i Identifier ) Long () string {
43- name := normalize (string (i ), maximumLongLength - shortLength - uint32 (len ("tpi---" )))
44- digest := hash (name , shortLength / 2 )
45-
46- return fmt .Sprintf ("tpi-%s-%s-%s" , name , digest , hash (name + digest , shortLength / 2 ))
61+ name := normalize (i .name , nameLength )
62+ return fmt .Sprintf ("tpi-%s-%s-%s" , name , i .salt , hash (name + i .salt , shortLength / 2 ))
4763}
4864
4965func (i Identifier ) Short () string {
@@ -52,9 +68,9 @@ func (i Identifier) Short() string {
5268}
5369
5470// hash deterministically generates a Base36 digest of `size`
55- // characters using `identifier` as the seed.
56- func hash (identifier string , size uint8 ) string {
57- digest := sha256 .Sum256 ([]byte (identifier ))
71+ // characters using the provided seed.
72+ func hash (seed string , size uint8 ) string {
73+ digest := sha256 .Sum256 ([]byte (seed ))
5874 random := uid .NewRandCustom (bytes .NewReader (digest [:]))
5975 encoder := uid .NewEncoderBase36 ()
6076 provider := uid .NewProviderCustom (sha256 .Size , random , encoder )
0 commit comments