// Package uid provides a compact 64-bit identifier with base62 string form. // // The scheme is used consistently across commerce services (cart, order, // profile, actor grains) so ids are URL/cookie-friendly and comparable // across system boundaries. Each domain re-exports the type as its own // named alias (e.g. order.OrderId = uid.ID) and keeps thin wrapper // functions so call-site code never needs to import platform/uid directly. package uid import ( "crypto/rand" "encoding/json" "fmt" ) // ID is a 64-bit identifier encoded as base62 text (max 11 chars). type ID uint64 const base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var base62Rev [256]byte func init() { for i := range base62Rev { base62Rev[i] = 0xFF } for i := 0; i < len(base62Alphabet); i++ { base62Rev[base62Alphabet[i]] = byte(i) } } // String returns the canonical base62 encoding. func (id ID) String() string { return encodeBase62(uint64(id)) } // MarshalJSON encodes the id as a JSON string. func (id ID) MarshalJSON() ([]byte, error) { return json.Marshal(id.String()) } // UnmarshalJSON decodes the id from a JSON string containing base62 text. func (id *ID) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } parsed, ok := Parse(s) if !ok { return fmt.Errorf("invalid uid: %q", s) } *id = parsed return nil } // New generates a cryptographically random non-zero 64-bit id. func New() (ID, error) { var b [8]byte if _, err := rand.Read(b[:]); err != nil { return 0, fmt.Errorf("uid.New: %w", err) } u := (uint64(b[0]) << 56) | (uint64(b[1]) << 48) | (uint64(b[2]) << 40) | (uint64(b[3]) << 32) | (uint64(b[4]) << 24) | (uint64(b[5]) << 16) | (uint64(b[6]) << 8) | uint64(b[7]) if u == 0 { return New() } return ID(u), nil } // MustNew panics if generation fails. func MustNew() ID { id, err := New() if err != nil { panic(err) } return id } // Parse parses a base62 string into an ID. Returns (0, false) for invalid input. func Parse(s string) (ID, bool) { if len(s) == 0 || len(s) > 11 { return 0, false } u, ok := decodeBase62(s) if !ok { return 0, false } return ID(u), true } // MustParse panics on invalid base62 input. func MustParse(s string) ID { id, ok := Parse(s) if !ok { panic(fmt.Sprintf("invalid uid: %q", s)) } return id } // encodeBase62 converts a uint64 to base62 (shortest form, no leading zeros). func encodeBase62(u uint64) string { if u == 0 { return "0" } var buf [11]byte i := len(buf) for u > 0 { i-- buf[i] = base62Alphabet[u%62] u /= 62 } return string(buf[i:]) } // decodeBase62 converts base62 text to uint64. func decodeBase62(s string) (uint64, bool) { var v uint64 for i := 0; i < len(s); i++ { d := base62Rev[s[i]] if d == 0xFF { return 0, false } v = v*62 + uint64(d) } return v, true }