From dcbbb15aafc7bae116e4530afa8a69b32ebddfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 14:42:00 +0200 Subject: [PATCH 1/8] feat: add VariablePrefixer interface and AnyPrefix type --- typeid.go | 17 +++++++++++++++++ typeid_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/typeid.go b/typeid.go index 468de56..2cbc370 100644 --- a/typeid.go +++ b/typeid.go @@ -11,6 +11,23 @@ type Prefixer interface { Prefix() string } +// VariablePrefixer is optionally implemented by prefix types that accept +// multiple string representations (e.g. "api_key" and "api_key_sandbox"). +// ParsePrefix sets the receiver to the variant matching s and reports success. +type VariablePrefixer interface { + ParsePrefix(s string) bool +} + +// AnyPrefix accepts any prefix string. Use it as the type parameter for +// [AnyUUID] or [AnyInt64] when the set of valid prefixes is not known at +// compile time: +// +// type FlexID = typeid.AnyUUID[typeid.AnyPrefix] +type AnyPrefix string + +func (p AnyPrefix) Prefix() string { return string(p) } +func (p *AnyPrefix) ParsePrefix(s string) bool { *p = AnyPrefix(s); return true } + var ( ErrOnlyV7 = errors.New("typeid: only UUIDv7 is supported") ErrZeroUUID = errors.New("typeid: zero UUID") diff --git a/typeid_test.go b/typeid_test.go index 3d488df..9182b5a 100644 --- a/typeid_test.go +++ b/typeid_test.go @@ -26,6 +26,36 @@ type ( OrgID = typeid.Int64[orgPrefix] ) +// Variable prefix test types. + +type apiKeyMode uint8 + +const ( + apiKeyLive apiKeyMode = iota + apiKeySandbox +) + +func (p apiKeyMode) Prefix() string { + switch p { + case apiKeySandbox: + return "api_key_sandbox" + default: + return "api_key" + } +} + +func (p *apiKeyMode) ParsePrefix(s string) bool { + switch s { + case "api_key": + *p = apiKeyLive + return true + case "api_key_sandbox": + *p = apiKeySandbox + return true + } + return false +} + // Compile-time interface checks. var ( _ fmt.Stringer = UserID{} @@ -48,6 +78,10 @@ var ( _ sql.Scanner = (*OrgID)(nil) _ sql.Scanner = (*typeid.AnyUUID)(nil) _ sql.Scanner = (*typeid.AnyInt64)(nil) + _ typeid.Prefixer = typeid.AnyPrefix("") + _ typeid.VariablePrefixer = (*typeid.AnyPrefix)(nil) + _ typeid.Prefixer = apiKeyMode(0) + _ typeid.VariablePrefixer = (*apiKeyMode)(nil) ) func Example() { From 105d53eeb395f47470956a5a5faa758f2151cb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 14:43:32 +0200 Subject: [PATCH 2/8] feat: generify AnyUUID[P Prefixer] with VariablePrefixer support --- anyuuid.go | 66 +++++++++++++++++++++++++++++++----------------------- uuid.go | 4 ++-- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/anyuuid.go b/anyuuid.go index 6272f9b..ffad083 100644 --- a/anyuuid.go +++ b/anyuuid.go @@ -12,69 +12,79 @@ import ( // AnyUUID is a UUIDv7 identifier with a runtime-configurable prefix. // Unlike [UUID], the prefix is not fixed at compile time. -type AnyUUID struct { +// +// Use [AnyPrefix] as P for unconstrained prefixes, or a custom enum type +// implementing [VariablePrefixer] for a known set of variants. +type AnyUUID[P Prefixer] struct { + prefix P val uuid.UUID - prefix string } -func NewAnyUUID(prefix string) (AnyUUID, error) { +func NewAnyUUID[P Prefixer](p P) (AnyUUID[P], error) { u, err := uuid.NewV7() if err != nil { - return AnyUUID{}, err + return AnyUUID[P]{}, err } - return AnyUUID{val: u, prefix: prefix}, nil + return AnyUUID[P]{val: u, prefix: p}, nil } -func AnyUUIDFrom(prefix string, u uuid.UUID) (AnyUUID, error) { +func AnyUUIDFrom[P Prefixer](p P, u uuid.UUID) (AnyUUID[P], error) { if u.Version() != 7 { - return AnyUUID{}, ErrOnlyV7 + return AnyUUID[P]{}, ErrOnlyV7 } - return AnyUUID{val: u, prefix: prefix}, nil + return AnyUUID[P]{val: u, prefix: p}, nil } -func ParseAnyUUID(s string) (AnyUUID, error) { +func ParseAnyUUID[P Prefixer](s string) (AnyUUID[P], error) { + var p P j := strings.LastIndex(s, "_") + 1 pref, suffix := s[:max(0, j-1)], s[j:] if len(suffix) != uuidSuffixLen { - return AnyUUID{}, fmt.Errorf("typeid: invalid format: %q", s) + return AnyUUID[P]{}, fmt.Errorf("typeid: invalid format: %q", s) + } + if vp, ok := any(&p).(VariablePrefixer); ok { + if !vp.ParsePrefix(pref) { + return AnyUUID[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + } + } else if p.Prefix() != pref { + return AnyUUID[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) } b, err := decodeBase32UUID(suffix) if err != nil { - return AnyUUID{}, err + return AnyUUID[P]{}, err } u := uuid.UUID(b) if u.Version() != 7 { - return AnyUUID{}, ErrOnlyV7 + return AnyUUID[P]{}, ErrOnlyV7 } - return AnyUUID{val: u, prefix: pref}, nil + return AnyUUID[P]{val: u, prefix: p}, nil } -func (id AnyUUID) UUID() uuid.UUID { return id.val } -func (id AnyUUID) Prefix() string { return id.prefix } -func (id *AnyUUID) SetPrefix(s string) { - id.prefix = s -} +func (id AnyUUID[P]) UUID() uuid.UUID { return id.val } +func (id AnyUUID[P]) Prefix() string { return id.prefix.Prefix() } +func (id AnyUUID[P]) PrefixValue() P { return id.prefix } +func (id *AnyUUID[P]) SetPrefix(p P) { id.prefix = p } -func (id AnyUUID) appendText(dst []byte) []byte { - return appendBase32UUID(dst, id.prefix, id.val) +func (id AnyUUID[P]) appendText(dst []byte) []byte { + return appendBase32UUID(dst, id.prefix.Prefix(), id.val) } -func (id AnyUUID) String() string { +func (id AnyUUID[P]) String() string { var buf [64]byte return string(id.appendText(buf[:0])) } -func (id AnyUUID) IsZero() bool { return id.val == uuid.UUID{} } +func (id AnyUUID[P]) IsZero() bool { return id.val == uuid.UUID{} } -func (id AnyUUID) MarshalText() ([]byte, error) { +func (id AnyUUID[P]) MarshalText() ([]byte, error) { if id.IsZero() { return nil, ErrZeroUUID } return id.appendText(nil), nil } -func (id *AnyUUID) UnmarshalText(data []byte) error { - parsed, err := ParseAnyUUID(string(data)) +func (id *AnyUUID[P]) UnmarshalText(data []byte) error { + parsed, err := ParseAnyUUID[P](string(data)) if err != nil { return err } @@ -82,14 +92,14 @@ func (id *AnyUUID) UnmarshalText(data []byte) error { return nil } -func (id AnyUUID) Value() (driver.Value, error) { +func (id AnyUUID[P]) Value() (driver.Value, error) { if id.IsZero() { return nil, ErrZeroUUID } return id.val.String(), nil } -func (id *AnyUUID) Scan(src any) (err error) { +func (id *AnyUUID[P]) Scan(src any) (err error) { var u uuid.UUID switch v := src.(type) { case string: @@ -118,7 +128,7 @@ func (id *AnyUUID) Scan(src any) (err error) { } // GetTime extracts the millisecond-precision creation timestamp from the UUIDv7. -func (id AnyUUID) GetTime() time.Time { +func (id AnyUUID[P]) GetTime() time.Time { ms := int64(binary.BigEndian.Uint16(id.val[:2]))<<32 | int64(binary.BigEndian.Uint32(id.val[2:6])) return time.UnixMilli(ms) } diff --git a/uuid.go b/uuid.go index 2b19277..f17dbd4 100644 --- a/uuid.go +++ b/uuid.go @@ -78,9 +78,9 @@ func (id UUID[P]) Value() (driver.Value, error) { } // Any converts a typed UUID to an AnyUUID with the same prefix and value. -func (id UUID[P]) Any() AnyUUID { +func (id UUID[P]) Any() AnyUUID[AnyPrefix] { var p P - return AnyUUID{val: id.val, prefix: p.Prefix()} + return AnyUUID[AnyPrefix]{val: id.val, prefix: AnyPrefix(p.Prefix())} } func (id *UUID[P]) Scan(src any) (err error) { From d0a78fd80e6444d0b37cb9586d56cc859746e0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 14:45:10 +0200 Subject: [PATCH 3/8] feat: generify AnyInt64[P Prefixer] with VariablePrefixer support --- anyint64.go | 64 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/anyint64.go b/anyint64.go index 6634464..30a36ad 100644 --- a/anyint64.go +++ b/anyint64.go @@ -11,72 +11,82 @@ import ( // AnyInt64 is a compact identifier with a runtime-configurable prefix. // Unlike [Int64], the prefix is not fixed at compile time. -type AnyInt64 struct { +// +// Use [AnyPrefix] as P for unconstrained prefixes, or a custom enum type +// implementing [VariablePrefixer] for a known set of variants. +type AnyInt64[P Prefixer] struct { + prefix P val int64 - prefix string } -func NewAnyInt64(prefix string) (AnyInt64, error) { +func NewAnyInt64[P Prefixer](p P) (AnyInt64[P], error) { ms := time.Now().UnixMilli() var rb [2]byte if _, err := rand.Read(rb[:]); err != nil { - return AnyInt64{}, fmt.Errorf("typeid: crypto/rand: %w", err) + return AnyInt64[P]{}, fmt.Errorf("typeid: crypto/rand: %w", err) } r := int64(binary.BigEndian.Uint16(rb[:]) & 0x7FFF) - return AnyInt64{val: (ms << randomBits) | r, prefix: prefix}, nil + return AnyInt64[P]{val: (ms << randomBits) | r, prefix: p}, nil } -func AnyInt64From(prefix string, v int64) (AnyInt64, error) { +func AnyInt64From[P Prefixer](p P, v int64) (AnyInt64[P], error) { if v <= 0 { - return AnyInt64{}, ErrNonPositiveInt + return AnyInt64[P]{}, ErrNonPositiveInt } - return AnyInt64{val: v, prefix: prefix}, nil + return AnyInt64[P]{val: v, prefix: p}, nil } -func ParseAnyInt64(s string) (AnyInt64, error) { +func ParseAnyInt64[P Prefixer](s string) (AnyInt64[P], error) { + var p P j := strings.LastIndex(s, "_") + 1 pref, suffix := s[:max(0, j-1)], s[j:] if len(suffix) != int64SuffixLen { - return AnyInt64{}, fmt.Errorf("typeid: invalid format: %q", s) + return AnyInt64[P]{}, fmt.Errorf("typeid: invalid format: %q", s) + } + if vp, ok := any(&p).(VariablePrefixer); ok { + if !vp.ParsePrefix(pref) { + return AnyInt64[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + } + } else if p.Prefix() != pref { + return AnyInt64[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) } v, err := decodeBase32Int64(suffix) if err != nil { - return AnyInt64{}, err + return AnyInt64[P]{}, err } if v <= 0 { - return AnyInt64{}, ErrNonPositiveInt + return AnyInt64[P]{}, ErrNonPositiveInt } - return AnyInt64{val: v, prefix: pref}, nil + return AnyInt64[P]{val: v, prefix: p}, nil } -func (id AnyInt64) Int64() int64 { return id.val } -func (id AnyInt64) Prefix() string { return id.prefix } -func (id *AnyInt64) SetPrefix(s string) { - id.prefix = s -} +func (id AnyInt64[P]) Int64() int64 { return id.val } +func (id AnyInt64[P]) Prefix() string { return id.prefix.Prefix() } +func (id AnyInt64[P]) PrefixValue() P { return id.prefix } +func (id *AnyInt64[P]) SetPrefix(p P) { id.prefix = p } -func (id AnyInt64) appendText(dst []byte) []byte { - return appendBase32Int64(dst, id.prefix, id.val) +func (id AnyInt64[P]) appendText(dst []byte) []byte { + return appendBase32Int64(dst, id.prefix.Prefix(), id.val) } -func (id AnyInt64) String() string { +func (id AnyInt64[P]) String() string { var buf [64]byte return string(id.appendText(buf[:0])) } -func (id AnyInt64) IsZero() bool { return id.val == 0 } +func (id AnyInt64[P]) IsZero() bool { return id.val == 0 } -func (id AnyInt64) MarshalText() ([]byte, error) { +func (id AnyInt64[P]) MarshalText() ([]byte, error) { if id.val <= 0 { return nil, ErrNonPositiveInt } return id.appendText(nil), nil } -func (id *AnyInt64) UnmarshalText(data []byte) error { - parsed, err := ParseAnyInt64(string(data)) +func (id *AnyInt64[P]) UnmarshalText(data []byte) error { + parsed, err := ParseAnyInt64[P](string(data)) if err != nil { return err } @@ -84,14 +94,14 @@ func (id *AnyInt64) UnmarshalText(data []byte) error { return nil } -func (id AnyInt64) Value() (driver.Value, error) { +func (id AnyInt64[P]) Value() (driver.Value, error) { if id.val <= 0 { return nil, ErrNonPositiveInt } return id.val, nil } -func (id *AnyInt64) Scan(src any) error { +func (id *AnyInt64[P]) Scan(src any) error { var v int64 switch sv := src.(type) { case int64: From a88724140404a35b6d9627e2adc012f9def59925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 14:49:48 +0200 Subject: [PATCH 4/8] test: update AnyUUID tests for generic AnyUUID[AnyPrefix] Also fixes the four AnyInt64 lines in int64_test.go that blocked compilation. --- int64_test.go | 8 +++--- typeid_test.go | 20 +++++++------- uuid_test.go | 72 +++++++++++++++++++++++++------------------------- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/int64_test.go b/int64_test.go index 16ba40f..8fea892 100644 --- a/int64_test.go +++ b/int64_test.go @@ -242,7 +242,7 @@ func BenchmarkInt64_Parse(b *testing.B) { func ExampleAnyInt64_switchToTypedInt64() { const payload = `{"id":"org_01hf7yat00c1s"}` type Request struct { - ID typeid.AnyInt64 `json:"id"` + ID typeid.AnyInt64[typeid.AnyPrefix] `json:"id"` } var req Request if err := json.Unmarshal([]byte(payload), &req); err != nil { @@ -270,7 +270,7 @@ func ExampleAnyInt64_switchToTypedInt64() { func TestAnyInt64_json(t *testing.T) { type Request struct { - ID typeid.AnyInt64 `json:"id"` + ID typeid.AnyInt64[typeid.AnyPrefix] `json:"id"` } suffix := "01hf7yat00c1s" @@ -292,7 +292,7 @@ func TestAnyInt64_json(t *testing.T) { func TestAnyInt64_prefixAndSetPrefix(t *testing.T) { suffix := "01hf7yat00c1s" - id, err := typeid.ParseAnyInt64("foo_" + suffix) + id, err := typeid.ParseAnyInt64[typeid.AnyPrefix]("foo_" + suffix) if err != nil { t.Fatal(err) } @@ -312,7 +312,7 @@ func TestAnyInt64_prefixAndSetPrefix(t *testing.T) { func TestAnyInt64_narrowToOrgPrefix(t *testing.T) { suffix := "01hf7yat00c1s" - anyID, err := typeid.ParseAnyInt64("org_" + suffix) + anyID, err := typeid.ParseAnyInt64[typeid.AnyPrefix]("org_" + suffix) if err != nil { t.Fatal(err) } diff --git a/typeid_test.go b/typeid_test.go index 9182b5a..6b0b450 100644 --- a/typeid_test.go +++ b/typeid_test.go @@ -60,24 +60,24 @@ func (p *apiKeyMode) ParsePrefix(s string) bool { var ( _ fmt.Stringer = UserID{} _ fmt.Stringer = OrgID{} - _ fmt.Stringer = typeid.AnyUUID{} - _ fmt.Stringer = typeid.AnyInt64{} + _ fmt.Stringer = typeid.AnyUUID[typeid.AnyPrefix]{} + _ fmt.Stringer = typeid.AnyInt64[typeid.AnyPrefix]{} _ encoding.TextMarshaler = UserID{} _ encoding.TextMarshaler = OrgID{} - _ encoding.TextMarshaler = typeid.AnyUUID{} - _ encoding.TextMarshaler = typeid.AnyInt64{} + _ encoding.TextMarshaler = typeid.AnyUUID[typeid.AnyPrefix]{} + _ encoding.TextMarshaler = typeid.AnyInt64[typeid.AnyPrefix]{} _ encoding.TextUnmarshaler = (*UserID)(nil) _ encoding.TextUnmarshaler = (*OrgID)(nil) - _ encoding.TextUnmarshaler = (*typeid.AnyUUID)(nil) - _ encoding.TextUnmarshaler = (*typeid.AnyInt64)(nil) + _ encoding.TextUnmarshaler = (*typeid.AnyUUID[typeid.AnyPrefix])(nil) + _ encoding.TextUnmarshaler = (*typeid.AnyInt64[typeid.AnyPrefix])(nil) _ driver.Valuer = UserID{} _ driver.Valuer = OrgID{} - _ driver.Valuer = typeid.AnyUUID{} - _ driver.Valuer = typeid.AnyInt64{} + _ driver.Valuer = typeid.AnyUUID[typeid.AnyPrefix]{} + _ driver.Valuer = typeid.AnyInt64[typeid.AnyPrefix]{} _ sql.Scanner = (*UserID)(nil) _ sql.Scanner = (*OrgID)(nil) - _ sql.Scanner = (*typeid.AnyUUID)(nil) - _ sql.Scanner = (*typeid.AnyInt64)(nil) + _ sql.Scanner = (*typeid.AnyUUID[typeid.AnyPrefix])(nil) + _ sql.Scanner = (*typeid.AnyInt64[typeid.AnyPrefix])(nil) _ typeid.Prefixer = typeid.AnyPrefix("") _ typeid.VariablePrefixer = (*typeid.AnyPrefix)(nil) _ typeid.Prefixer = apiKeyMode(0) diff --git a/uuid_test.go b/uuid_test.go index 9d32038..34f2970 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -260,7 +260,7 @@ func BenchmarkUUID_Parse(b *testing.B) { func TestAnyUUID_json(t *testing.T) { type Request struct { - ID typeid.AnyUUID `json:"id"` + ID typeid.AnyUUID[typeid.AnyPrefix] `json:"id"` } suffix := "01jcp1ss00edg828t5cy4tqkff" @@ -285,7 +285,7 @@ func TestAnyUUID_json(t *testing.T) { func ExampleAnyUUID_switchToTypedUUID() { const payload = `{"id":"user_01jcp1ss00edg828t5cy4tqkff"}` type Request struct { - ID typeid.AnyUUID `json:"id"` + ID typeid.AnyUUID[typeid.AnyPrefix] `json:"id"` } var req Request if err := json.Unmarshal([]byte(payload), &req); err != nil { @@ -313,7 +313,7 @@ func ExampleAnyUUID_switchToTypedUUID() { func TestAnyUUID_narrowToUserPrefix(t *testing.T) { suffix := "01jcp1ss00edg828t5cy4tqkff" - anyID, err := typeid.ParseAnyUUID("user_" + suffix) + anyID, err := typeid.ParseAnyUUID[typeid.AnyPrefix]("user_" + suffix) if err != nil { t.Fatal(err) } @@ -337,7 +337,7 @@ func TestAnyUUID_narrowToUserPrefix(t *testing.T) { func TestAnyUUID_prefixAndSetPrefix(t *testing.T) { suffix := "01jcp1ss00edg828t5cy4tqkff" - id, err := typeid.ParseAnyUUID("foo_" + suffix) + id, err := typeid.ParseAnyUUID[typeid.AnyPrefix]("foo_" + suffix) if err != nil { t.Fatal(err) } @@ -345,7 +345,7 @@ func TestAnyUUID_prefixAndSetPrefix(t *testing.T) { t.Fatalf("Prefix() = %q, want foo", got) } - id.SetPrefix("bar") + id.SetPrefix(typeid.AnyPrefix("bar")) if got := id.Prefix(); got != "bar" { t.Fatalf("after SetPrefix, Prefix() = %q, want bar", got) } @@ -356,7 +356,7 @@ func TestAnyUUID_prefixAndSetPrefix(t *testing.T) { } func TestNewAnyUUID(t *testing.T) { - id, err := typeid.NewAnyUUID("user") + id, err := typeid.NewAnyUUID(typeid.AnyPrefix("user")) if err != nil { t.Fatal(err) } @@ -373,7 +373,7 @@ func TestNewAnyUUID(t *testing.T) { func TestAnyUUIDFrom(t *testing.T) { raw := uuid.Must(uuid.NewV7()) - id, err := typeid.AnyUUIDFrom("team", raw) + id, err := typeid.AnyUUIDFrom(typeid.AnyPrefix("team"), raw) if err != nil { t.Fatal(err) } @@ -387,14 +387,14 @@ func TestAnyUUIDFrom(t *testing.T) { func TestAnyUUIDFrom_RejectsV4(t *testing.T) { v4 := uuid.New() - _, err := typeid.AnyUUIDFrom("user", v4) + _, err := typeid.AnyUUIDFrom(typeid.AnyPrefix("user"), v4) if err == nil { t.Error("expected error for non-v7 UUID") } } func TestAnyUUID_String(t *testing.T) { - id, _ := typeid.NewAnyUUID("user") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("user")) s := id.String() if !strings.HasPrefix(s, "user_") { t.Errorf("expected user_ prefix, got %q", s) @@ -405,27 +405,27 @@ func TestAnyUUID_String(t *testing.T) { } func TestAnyUUID_SetPrefix(t *testing.T) { - id, _ := typeid.NewAnyUUID("apiKey") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) if !strings.HasPrefix(id.String(), "apiKey_") { t.Fatalf("expected apiKey_ prefix, got %q", id.String()) } - id.SetPrefix("apiKeySandbox") + id.SetPrefix(typeid.AnyPrefix("apiKeySandbox")) if !strings.HasPrefix(id.String(), "apiKeySandbox_") { t.Errorf("expected apiKeySandbox_ prefix after SetPrefix, got %q", id.String()) } // Underlying UUID unchanged - id2, _ := typeid.NewAnyUUID("apiKey") + id2, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) raw := id2.UUID() - id2.SetPrefix("other") + id2.SetPrefix(typeid.AnyPrefix("other")) if id2.UUID() != raw { t.Error("SetPrefix should not change the UUID") } } func TestAnyUUID_MarshalText_RejectsZero(t *testing.T) { - var id typeid.AnyUUID + var id typeid.AnyUUID[typeid.AnyPrefix] _, err := id.MarshalText() if err == nil { t.Error("MarshalText should reject zero") @@ -433,10 +433,10 @@ func TestAnyUUID_MarshalText_RejectsZero(t *testing.T) { } func TestAnyUUID_UnmarshalText(t *testing.T) { - original, _ := typeid.NewAnyUUID("proj") + original, _ := typeid.NewAnyUUID(typeid.AnyPrefix("proj")) data, _ := original.MarshalText() - var parsed typeid.AnyUUID + var parsed typeid.AnyUUID[typeid.AnyPrefix] if err := parsed.UnmarshalText(data); err != nil { t.Fatal(err) } @@ -449,10 +449,10 @@ func TestAnyUUID_UnmarshalText(t *testing.T) { } func TestAnyUUID_UnmarshalText_MultiWordPrefix(t *testing.T) { - original, _ := typeid.NewAnyUUID("apiKeySandbox") + original, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKeySandbox")) data, _ := original.MarshalText() - var parsed typeid.AnyUUID + var parsed typeid.AnyUUID[typeid.AnyPrefix] if err := parsed.UnmarshalText(data); err != nil { t.Fatal(err) } @@ -465,7 +465,7 @@ func TestAnyUUID_UnmarshalText_MultiWordPrefix(t *testing.T) { } func TestAnyUUID_Value(t *testing.T) { - id, _ := typeid.NewAnyUUID("key") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("key")) val, err := id.Value() if err != nil { t.Fatal(err) @@ -480,7 +480,7 @@ func TestAnyUUID_Value(t *testing.T) { } func TestAnyUUID_Value_RejectsZero(t *testing.T) { - var id typeid.AnyUUID + var id typeid.AnyUUID[typeid.AnyPrefix] _, err := id.Value() if err == nil { t.Error("Value should reject zero") @@ -488,10 +488,10 @@ func TestAnyUUID_Value_RejectsZero(t *testing.T) { } func TestAnyUUID_Scan(t *testing.T) { - original, _ := typeid.NewAnyUUID("user") + original, _ := typeid.NewAnyUUID(typeid.AnyPrefix("user")) raw := original.UUID().String() - var scanned typeid.AnyUUID + var scanned typeid.AnyUUID[typeid.AnyPrefix] if err := scanned.Scan(raw); err != nil { t.Fatal(err) } @@ -501,10 +501,10 @@ func TestAnyUUID_Scan(t *testing.T) { } func TestAnyUUID_ScanRawBytes(t *testing.T) { - original, _ := typeid.NewAnyUUID("user") + original, _ := typeid.NewAnyUUID(typeid.AnyPrefix("user")) raw := original.UUID() - var scanned typeid.AnyUUID + var scanned typeid.AnyUUID[typeid.AnyPrefix] if err := scanned.Scan(raw[:]); err != nil { t.Fatal(err) } @@ -514,7 +514,7 @@ func TestAnyUUID_ScanRawBytes(t *testing.T) { } func TestAnyUUID_ScanInvalid(t *testing.T) { - var id typeid.AnyUUID + var id typeid.AnyUUID[typeid.AnyPrefix] if err := id.Scan(123); err == nil { t.Error("Scan should reject int") } @@ -525,19 +525,19 @@ func TestAnyUUID_ScanInvalid(t *testing.T) { } func TestAnyUUID_DBRoundTrip(t *testing.T) { - id, _ := typeid.NewAnyUUID("apiKey") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) val, err := id.Value() if err != nil { t.Fatal(err) } - var scanned typeid.AnyUUID + var scanned typeid.AnyUUID[typeid.AnyPrefix] if err := scanned.Scan(val); err != nil { t.Fatal(err) } - scanned.SetPrefix("apiKeySandbox") + scanned.SetPrefix(typeid.AnyPrefix("apiKeySandbox")) if scanned.UUID() != id.UUID() { t.Error("UUID mismatch in round-trip") @@ -561,7 +561,7 @@ func TestUUID_Any(t *testing.T) { t.Errorf("String mismatch: any=%q, typed=%q", any.String(), typed.String()) } - any.SetPrefix("admin") + any.SetPrefix(typeid.AnyPrefix("admin")) if any.UUID() != typed.UUID() { t.Error("UUID changed after SetPrefix") } @@ -572,7 +572,7 @@ func TestUUID_Any(t *testing.T) { func TestAnyUUID_GetTime(t *testing.T) { before := time.Now() - id, _ := typeid.NewAnyUUID("user") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("user")) after := time.Now() got := id.GetTime() @@ -586,11 +586,11 @@ func TestAnyUUID_GetTime(t *testing.T) { func TestAnyUUID_JSON(t *testing.T) { type Record struct { - ID typeid.AnyUUID `json:"id"` - Name string `json:"name"` + ID typeid.AnyUUID[typeid.AnyPrefix] `json:"id"` + Name string `json:"name"` } - id, _ := typeid.NewAnyUUID("apiKey") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) original := Record{ID: id, Name: "test"} data, err := json.Marshal(original) if err != nil { @@ -613,7 +613,7 @@ func TestAnyUUID_JSON(t *testing.T) { } func BenchmarkAnyUUID_String(b *testing.B) { - id, _ := typeid.NewAnyUUID("apiKeySandbox") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKeySandbox")) b.ResetTimer() for b.Loop() { _ = id.String() @@ -621,11 +621,11 @@ func BenchmarkAnyUUID_String(b *testing.B) { } func BenchmarkAnyUUID_Parse(b *testing.B) { - id, _ := typeid.NewAnyUUID("apiKeySandbox") + id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKeySandbox")) s := id.String() b.ResetTimer() for b.Loop() { - typeid.ParseAnyUUID(s) //nolint:errcheck + typeid.ParseAnyUUID[typeid.AnyPrefix](s) //nolint:errcheck } } From 10b064a65be4e9161606ac3d75f985b47314dd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 14:52:39 +0200 Subject: [PATCH 5/8] test: add variable prefix tests for AnyUUID and AnyInt64 --- int64_test.go | 58 ++++++++++++++++++++++++++ uuid_test.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) diff --git a/int64_test.go b/int64_test.go index 8fea892..1b8053b 100644 --- a/int64_test.go +++ b/int64_test.go @@ -352,3 +352,61 @@ func TestInt64_Sortable(t *testing.T) { t.Errorf("expected a < b lexicographically\n a = %s\n b = %s", a, b) } } + +// -- Variable prefix (apiKeyMode) tests for Int64 -- + +type ApiKeyInt64ID = typeid.AnyInt64[apiKeyMode] + +func TestAnyInt64_VariablePrefix_Parse(t *testing.T) { + id, _ := typeid.NewAnyInt64(apiKeyLive) + s := id.String() + parsed, err := typeid.ParseAnyInt64[apiKeyMode](s) + if err != nil { + t.Fatalf("ParseAnyInt64: %v", err) + } + if parsed.PrefixValue() != apiKeyLive { + t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeyLive) + } + if parsed.Prefix() != "api_key" { + t.Errorf("Prefix() = %q, want %q", parsed.Prefix(), "api_key") + } +} + +func TestAnyInt64_VariablePrefix_RejectsUnknown(t *testing.T) { + id, _ := typeid.NewAnyInt64(apiKeyLive) + // Swap prefix to something unknown + s := strings.Replace(id.String(), "api_key_", "bogus_", 1) + _, err := typeid.ParseAnyInt64[apiKeyMode](s) + if err == nil { + t.Fatal("expected error for unknown prefix") + } +} + +func TestAnyInt64_VariablePrefix_Roundtrip(t *testing.T) { + id, _ := typeid.NewAnyInt64(apiKeySandbox) + s := id.String() + parsed, err := typeid.ParseAnyInt64[apiKeyMode](s) + if err != nil { + t.Fatalf("roundtrip: %v", err) + } + if parsed.Int64() != id.Int64() { + t.Error("Int64 mismatch") + } + if parsed.PrefixValue() != apiKeySandbox { + t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeySandbox) + } + if parsed.String() != s { + t.Errorf("String() = %q, want %q", parsed.String(), s) + } +} + +func TestAnyInt64_VariablePrefix_SetPrefix(t *testing.T) { + id, _ := typeid.NewAnyInt64(apiKeyLive) + id.SetPrefix(apiKeySandbox) + if id.PrefixValue() != apiKeySandbox { + t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeySandbox) + } + if id.Prefix() != "api_key_sandbox" { + t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key_sandbox") + } +} diff --git a/uuid_test.go b/uuid_test.go index 34f2970..aaf60c1 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -643,3 +643,115 @@ func TestUUID_Sortable(t *testing.T) { t.Errorf("expected a < b (IDs must sort by time)\n a = %s\n b = %s", a, b) } } + +// -- Variable prefix (apiKeyMode) tests -- + +type ApiKeyID = typeid.AnyUUID[apiKeyMode] + +func TestAnyUUID_VariablePrefix_Parse(t *testing.T) { + suffix := "01jcp1ss00edg828t5cy4tqkff" + + tests := []struct { + input string + wantPrefix string + wantMode apiKeyMode + }{ + {"api_key_" + suffix, "api_key", apiKeyLive}, + {"api_key_sandbox_" + suffix, "api_key_sandbox", apiKeySandbox}, + } + for _, tt := range tests { + t.Run(tt.wantPrefix, func(t *testing.T) { + id, err := typeid.ParseAnyUUID[apiKeyMode](tt.input) + if err != nil { + t.Fatalf("ParseAnyUUID: %v", err) + } + if id.Prefix() != tt.wantPrefix { + t.Errorf("Prefix() = %q, want %q", id.Prefix(), tt.wantPrefix) + } + if id.PrefixValue() != tt.wantMode { + t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), tt.wantMode) + } + }) + } +} + +func TestAnyUUID_VariablePrefix_RejectsUnknown(t *testing.T) { + suffix := "01jcp1ss00edg828t5cy4tqkff" + _, err := typeid.ParseAnyUUID[apiKeyMode]("bogus_" + suffix) + if err == nil { + t.Fatal("expected error for unknown prefix") + } +} + +func TestAnyUUID_VariablePrefix_Roundtrip(t *testing.T) { + id, err := typeid.NewAnyUUID(apiKeySandbox) + if err != nil { + t.Fatal(err) + } + s := id.String() + parsed, err := typeid.ParseAnyUUID[apiKeyMode](s) + if err != nil { + t.Fatalf("ParseAnyUUID roundtrip: %v", err) + } + if parsed.UUID() != id.UUID() { + t.Error("UUID mismatch") + } + if parsed.PrefixValue() != apiKeySandbox { + t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeySandbox) + } + if parsed.String() != s { + t.Errorf("String() = %q, want %q", parsed.String(), s) + } +} + +func TestAnyUUID_VariablePrefix_SetPrefix(t *testing.T) { + id, _ := typeid.NewAnyUUID(apiKeyLive) + if id.PrefixValue() != apiKeyLive { + t.Fatalf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeyLive) + } + + id.SetPrefix(apiKeySandbox) + if id.PrefixValue() != apiKeySandbox { + t.Errorf("after SetPrefix, PrefixValue() = %d, want %d", id.PrefixValue(), apiKeySandbox) + } + if id.Prefix() != "api_key_sandbox" { + t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key_sandbox") + } +} + +func TestAnyUUID_VariablePrefix_JSON(t *testing.T) { + type Record struct { + ID ApiKeyID `json:"id"` + } + + id, _ := typeid.NewAnyUUID(apiKeySandbox) + original := Record{ID: id} + data, err := json.Marshal(original) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(data), `"id":"api_key_sandbox_`) { + t.Errorf("JSON should contain api_key_sandbox_ prefix: %s", data) + } + + var decoded Record + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatal(err) + } + if decoded.ID.PrefixValue() != apiKeySandbox { + t.Errorf("PrefixValue() = %d, want %d", decoded.ID.PrefixValue(), apiKeySandbox) + } + if decoded.ID.UUID() != original.ID.UUID() { + t.Error("UUID mismatch after JSON round-trip") + } +} + +func TestAnyUUID_VariablePrefix_DefaultVariant(t *testing.T) { + id, _ := typeid.NewAnyUUID(apiKeyLive) + if id.Prefix() != "api_key" { + t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key") + } + if id.PrefixValue() != apiKeyLive { + t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeyLive) + } +} From 4e0522f07499ddd70fef75b4101d72600096549e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 15:12:36 +0200 Subject: [PATCH 6/8] refactor: replace if/else with two guard clauses in prefix matching --- anyint64.go | 10 +++++----- anyuuid.go | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/anyint64.go b/anyint64.go index 30a36ad..b711b67 100644 --- a/anyint64.go +++ b/anyint64.go @@ -45,11 +45,11 @@ func ParseAnyInt64[P Prefixer](s string) (AnyInt64[P], error) { if len(suffix) != int64SuffixLen { return AnyInt64[P]{}, fmt.Errorf("typeid: invalid format: %q", s) } - if vp, ok := any(&p).(VariablePrefixer); ok { - if !vp.ParsePrefix(pref) { - return AnyInt64[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) - } - } else if p.Prefix() != pref { + vp, isVar := any(&p).(VariablePrefixer) + if isVar && !vp.ParsePrefix(pref) { + return AnyInt64[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + } + if !isVar && p.Prefix() != pref { return AnyInt64[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) } v, err := decodeBase32Int64(suffix) diff --git a/anyuuid.go b/anyuuid.go index ffad083..b275e82 100644 --- a/anyuuid.go +++ b/anyuuid.go @@ -42,11 +42,11 @@ func ParseAnyUUID[P Prefixer](s string) (AnyUUID[P], error) { if len(suffix) != uuidSuffixLen { return AnyUUID[P]{}, fmt.Errorf("typeid: invalid format: %q", s) } - if vp, ok := any(&p).(VariablePrefixer); ok { - if !vp.ParsePrefix(pref) { - return AnyUUID[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) - } - } else if p.Prefix() != pref { + vp, isVar := any(&p).(VariablePrefixer) + if isVar && !vp.ParsePrefix(pref) { + return AnyUUID[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + } + if !isVar && p.Prefix() != pref { return AnyUUID[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) } b, err := decodeBase32UUID(suffix) From af0c72938d1fbdbc4a6073228f8e8e3fb118e79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 15:38:25 +0200 Subject: [PATCH 7/8] refactor: unify prefix validation into single check --- anyint64.go | 9 ++++----- anyuuid.go | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/anyint64.go b/anyint64.go index b711b67..6a542c3 100644 --- a/anyint64.go +++ b/anyint64.go @@ -45,12 +45,11 @@ func ParseAnyInt64[P Prefixer](s string) (AnyInt64[P], error) { if len(suffix) != int64SuffixLen { return AnyInt64[P]{}, fmt.Errorf("typeid: invalid format: %q", s) } - vp, isVar := any(&p).(VariablePrefixer) - if isVar && !vp.ParsePrefix(pref) { - return AnyInt64[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + if vp, ok := any(&p).(VariablePrefixer); ok { + vp.ParsePrefix(pref) } - if !isVar && p.Prefix() != pref { - return AnyInt64[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) + if p.Prefix() != pref { + return AnyInt64[P]{}, fmt.Errorf("typeid: invalid prefix: %q", pref) } v, err := decodeBase32Int64(suffix) if err != nil { diff --git a/anyuuid.go b/anyuuid.go index b275e82..d49982b 100644 --- a/anyuuid.go +++ b/anyuuid.go @@ -42,12 +42,11 @@ func ParseAnyUUID[P Prefixer](s string) (AnyUUID[P], error) { if len(suffix) != uuidSuffixLen { return AnyUUID[P]{}, fmt.Errorf("typeid: invalid format: %q", s) } - vp, isVar := any(&p).(VariablePrefixer) - if isVar && !vp.ParsePrefix(pref) { - return AnyUUID[P]{}, fmt.Errorf("typeid: unknown prefix: %q", pref) + if vp, ok := any(&p).(VariablePrefixer); ok { + vp.ParsePrefix(pref) } - if !isVar && p.Prefix() != pref { - return AnyUUID[P]{}, fmt.Errorf("typeid: prefix mismatch: expected %q, got %q", p.Prefix(), pref) + if p.Prefix() != pref { + return AnyUUID[P]{}, fmt.Errorf("typeid: invalid prefix: %q", pref) } b, err := decodeBase32UUID(suffix) if err != nil { From 386437212788ea705e507b21d37d3cb30e1941b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20-=20=E3=82=A2=E3=83=AC=E3=83=83=E3=82=AF=E3=82=B9?= Date: Fri, 10 Apr 2026 15:49:28 +0200 Subject: [PATCH 8/8] refactor: rename PrefixValue() to Variant() --- anyint64.go | 2 +- anyuuid.go | 2 +- int64_test.go | 12 ++++++------ uuid_test.go | 24 ++++++++++++------------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/anyint64.go b/anyint64.go index 6a542c3..5dcb873 100644 --- a/anyint64.go +++ b/anyint64.go @@ -63,7 +63,7 @@ func ParseAnyInt64[P Prefixer](s string) (AnyInt64[P], error) { func (id AnyInt64[P]) Int64() int64 { return id.val } func (id AnyInt64[P]) Prefix() string { return id.prefix.Prefix() } -func (id AnyInt64[P]) PrefixValue() P { return id.prefix } +func (id AnyInt64[P]) Variant() P { return id.prefix } func (id *AnyInt64[P]) SetPrefix(p P) { id.prefix = p } func (id AnyInt64[P]) appendText(dst []byte) []byte { diff --git a/anyuuid.go b/anyuuid.go index d49982b..d21cdef 100644 --- a/anyuuid.go +++ b/anyuuid.go @@ -61,7 +61,7 @@ func ParseAnyUUID[P Prefixer](s string) (AnyUUID[P], error) { func (id AnyUUID[P]) UUID() uuid.UUID { return id.val } func (id AnyUUID[P]) Prefix() string { return id.prefix.Prefix() } -func (id AnyUUID[P]) PrefixValue() P { return id.prefix } +func (id AnyUUID[P]) Variant() P { return id.prefix } func (id *AnyUUID[P]) SetPrefix(p P) { id.prefix = p } func (id AnyUUID[P]) appendText(dst []byte) []byte { diff --git a/int64_test.go b/int64_test.go index 1b8053b..c70afea 100644 --- a/int64_test.go +++ b/int64_test.go @@ -364,8 +364,8 @@ func TestAnyInt64_VariablePrefix_Parse(t *testing.T) { if err != nil { t.Fatalf("ParseAnyInt64: %v", err) } - if parsed.PrefixValue() != apiKeyLive { - t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeyLive) + if parsed.Variant() != apiKeyLive { + t.Errorf("Variant() = %d, want %d", parsed.Variant(), apiKeyLive) } if parsed.Prefix() != "api_key" { t.Errorf("Prefix() = %q, want %q", parsed.Prefix(), "api_key") @@ -392,8 +392,8 @@ func TestAnyInt64_VariablePrefix_Roundtrip(t *testing.T) { if parsed.Int64() != id.Int64() { t.Error("Int64 mismatch") } - if parsed.PrefixValue() != apiKeySandbox { - t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeySandbox) + if parsed.Variant() != apiKeySandbox { + t.Errorf("Variant() = %d, want %d", parsed.Variant(), apiKeySandbox) } if parsed.String() != s { t.Errorf("String() = %q, want %q", parsed.String(), s) @@ -403,8 +403,8 @@ func TestAnyInt64_VariablePrefix_Roundtrip(t *testing.T) { func TestAnyInt64_VariablePrefix_SetPrefix(t *testing.T) { id, _ := typeid.NewAnyInt64(apiKeyLive) id.SetPrefix(apiKeySandbox) - if id.PrefixValue() != apiKeySandbox { - t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeySandbox) + if id.Variant() != apiKeySandbox { + t.Errorf("Variant() = %d, want %d", id.Variant(), apiKeySandbox) } if id.Prefix() != "api_key_sandbox" { t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key_sandbox") diff --git a/uuid_test.go b/uuid_test.go index aaf60c1..886c399 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -668,8 +668,8 @@ func TestAnyUUID_VariablePrefix_Parse(t *testing.T) { if id.Prefix() != tt.wantPrefix { t.Errorf("Prefix() = %q, want %q", id.Prefix(), tt.wantPrefix) } - if id.PrefixValue() != tt.wantMode { - t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), tt.wantMode) + if id.Variant() != tt.wantMode { + t.Errorf("Variant() = %d, want %d", id.Variant(), tt.wantMode) } }) } @@ -696,8 +696,8 @@ func TestAnyUUID_VariablePrefix_Roundtrip(t *testing.T) { if parsed.UUID() != id.UUID() { t.Error("UUID mismatch") } - if parsed.PrefixValue() != apiKeySandbox { - t.Errorf("PrefixValue() = %d, want %d", parsed.PrefixValue(), apiKeySandbox) + if parsed.Variant() != apiKeySandbox { + t.Errorf("Variant() = %d, want %d", parsed.Variant(), apiKeySandbox) } if parsed.String() != s { t.Errorf("String() = %q, want %q", parsed.String(), s) @@ -706,13 +706,13 @@ func TestAnyUUID_VariablePrefix_Roundtrip(t *testing.T) { func TestAnyUUID_VariablePrefix_SetPrefix(t *testing.T) { id, _ := typeid.NewAnyUUID(apiKeyLive) - if id.PrefixValue() != apiKeyLive { - t.Fatalf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeyLive) + if id.Variant() != apiKeyLive { + t.Fatalf("Variant() = %d, want %d", id.Variant(), apiKeyLive) } id.SetPrefix(apiKeySandbox) - if id.PrefixValue() != apiKeySandbox { - t.Errorf("after SetPrefix, PrefixValue() = %d, want %d", id.PrefixValue(), apiKeySandbox) + if id.Variant() != apiKeySandbox { + t.Errorf("after SetPrefix, Variant() = %d, want %d", id.Variant(), apiKeySandbox) } if id.Prefix() != "api_key_sandbox" { t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key_sandbox") @@ -738,8 +738,8 @@ func TestAnyUUID_VariablePrefix_JSON(t *testing.T) { if err := json.Unmarshal(data, &decoded); err != nil { t.Fatal(err) } - if decoded.ID.PrefixValue() != apiKeySandbox { - t.Errorf("PrefixValue() = %d, want %d", decoded.ID.PrefixValue(), apiKeySandbox) + if decoded.ID.Variant() != apiKeySandbox { + t.Errorf("Variant() = %d, want %d", decoded.ID.Variant(), apiKeySandbox) } if decoded.ID.UUID() != original.ID.UUID() { t.Error("UUID mismatch after JSON round-trip") @@ -751,7 +751,7 @@ func TestAnyUUID_VariablePrefix_DefaultVariant(t *testing.T) { if id.Prefix() != "api_key" { t.Errorf("Prefix() = %q, want %q", id.Prefix(), "api_key") } - if id.PrefixValue() != apiKeyLive { - t.Errorf("PrefixValue() = %d, want %d", id.PrefixValue(), apiKeyLive) + if id.Variant() != apiKeyLive { + t.Errorf("Variant() = %d, want %d", id.Variant(), apiKeyLive) } }