From 31c35f4472c233c0f3842173b8b3122e537ed7dd 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: Tue, 14 Apr 2026 11:03:14 +0200 Subject: [PATCH] Revert "feat: generic AnyUUID/AnyInt64 with variable prefix support (#7)" This reverts commit 94c57ef8c8f142b8d711e6a0c2a9361db67fcf18. --- anyint64.go | 63 ++++++++--------- anyuuid.go | 65 ++++++++--------- int64_test.go | 66 ++---------------- typeid.go | 17 ----- typeid_test.go | 54 +++------------ uuid.go | 4 +- uuid_test.go | 184 ++++++++++--------------------------------------- 7 files changed, 107 insertions(+), 346 deletions(-) diff --git a/anyint64.go b/anyint64.go index 5dcb873..6634464 100644 --- a/anyint64.go +++ b/anyint64.go @@ -11,81 +11,72 @@ import ( // AnyInt64 is a compact identifier with a runtime-configurable prefix. // Unlike [Int64], the prefix is not fixed at compile time. -// -// 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 +type AnyInt64 struct { val int64 + prefix string } -func NewAnyInt64[P Prefixer](p P) (AnyInt64[P], error) { +func NewAnyInt64(prefix string) (AnyInt64, error) { ms := time.Now().UnixMilli() var rb [2]byte if _, err := rand.Read(rb[:]); err != nil { - return AnyInt64[P]{}, fmt.Errorf("typeid: crypto/rand: %w", err) + return AnyInt64{}, fmt.Errorf("typeid: crypto/rand: %w", err) } r := int64(binary.BigEndian.Uint16(rb[:]) & 0x7FFF) - return AnyInt64[P]{val: (ms << randomBits) | r, prefix: p}, nil + return AnyInt64{val: (ms << randomBits) | r, prefix: prefix}, nil } -func AnyInt64From[P Prefixer](p P, v int64) (AnyInt64[P], error) { +func AnyInt64From(prefix string, v int64) (AnyInt64, error) { if v <= 0 { - return AnyInt64[P]{}, ErrNonPositiveInt + return AnyInt64{}, ErrNonPositiveInt } - return AnyInt64[P]{val: v, prefix: p}, nil + return AnyInt64{val: v, prefix: prefix}, nil } -func ParseAnyInt64[P Prefixer](s string) (AnyInt64[P], error) { - var p P +func ParseAnyInt64(s string) (AnyInt64, error) { j := strings.LastIndex(s, "_") + 1 pref, suffix := s[:max(0, j-1)], s[j:] if len(suffix) != int64SuffixLen { - return AnyInt64[P]{}, fmt.Errorf("typeid: invalid format: %q", s) - } - if vp, ok := any(&p).(VariablePrefixer); ok { - vp.ParsePrefix(pref) - } - if p.Prefix() != pref { - return AnyInt64[P]{}, fmt.Errorf("typeid: invalid prefix: %q", pref) + return AnyInt64{}, fmt.Errorf("typeid: invalid format: %q", s) } v, err := decodeBase32Int64(suffix) if err != nil { - return AnyInt64[P]{}, err + return AnyInt64{}, err } if v <= 0 { - return AnyInt64[P]{}, ErrNonPositiveInt + return AnyInt64{}, ErrNonPositiveInt } - return AnyInt64[P]{val: v, prefix: p}, nil + return AnyInt64{val: v, prefix: pref}, nil } -func (id AnyInt64[P]) Int64() int64 { return id.val } -func (id AnyInt64[P]) Prefix() string { return id.prefix.Prefix() } -func (id AnyInt64[P]) Variant() P { return id.prefix } -func (id *AnyInt64[P]) SetPrefix(p P) { id.prefix = p } +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]) appendText(dst []byte) []byte { - return appendBase32Int64(dst, id.prefix.Prefix(), id.val) +func (id AnyInt64) appendText(dst []byte) []byte { + return appendBase32Int64(dst, id.prefix, id.val) } -func (id AnyInt64[P]) String() string { +func (id AnyInt64) String() string { var buf [64]byte return string(id.appendText(buf[:0])) } -func (id AnyInt64[P]) IsZero() bool { return id.val == 0 } +func (id AnyInt64) IsZero() bool { return id.val == 0 } -func (id AnyInt64[P]) MarshalText() ([]byte, error) { +func (id AnyInt64) MarshalText() ([]byte, error) { if id.val <= 0 { return nil, ErrNonPositiveInt } return id.appendText(nil), nil } -func (id *AnyInt64[P]) UnmarshalText(data []byte) error { - parsed, err := ParseAnyInt64[P](string(data)) +func (id *AnyInt64) UnmarshalText(data []byte) error { + parsed, err := ParseAnyInt64(string(data)) if err != nil { return err } @@ -93,14 +84,14 @@ func (id *AnyInt64[P]) UnmarshalText(data []byte) error { return nil } -func (id AnyInt64[P]) Value() (driver.Value, error) { +func (id AnyInt64) Value() (driver.Value, error) { if id.val <= 0 { return nil, ErrNonPositiveInt } return id.val, nil } -func (id *AnyInt64[P]) Scan(src any) error { +func (id *AnyInt64) Scan(src any) error { var v int64 switch sv := src.(type) { case int64: diff --git a/anyuuid.go b/anyuuid.go index d21cdef..6272f9b 100644 --- a/anyuuid.go +++ b/anyuuid.go @@ -12,78 +12,69 @@ import ( // AnyUUID is a UUIDv7 identifier with a runtime-configurable prefix. // Unlike [UUID], the prefix is not fixed at compile time. -// -// 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 +type AnyUUID struct { val uuid.UUID + prefix string } -func NewAnyUUID[P Prefixer](p P) (AnyUUID[P], error) { +func NewAnyUUID(prefix string) (AnyUUID, error) { u, err := uuid.NewV7() if err != nil { - return AnyUUID[P]{}, err + return AnyUUID{}, err } - return AnyUUID[P]{val: u, prefix: p}, nil + return AnyUUID{val: u, prefix: prefix}, nil } -func AnyUUIDFrom[P Prefixer](p P, u uuid.UUID) (AnyUUID[P], error) { +func AnyUUIDFrom(prefix string, u uuid.UUID) (AnyUUID, error) { if u.Version() != 7 { - return AnyUUID[P]{}, ErrOnlyV7 + return AnyUUID{}, ErrOnlyV7 } - return AnyUUID[P]{val: u, prefix: p}, nil + return AnyUUID{val: u, prefix: prefix}, nil } -func ParseAnyUUID[P Prefixer](s string) (AnyUUID[P], error) { - var p P +func ParseAnyUUID(s string) (AnyUUID, error) { j := strings.LastIndex(s, "_") + 1 pref, suffix := s[:max(0, j-1)], s[j:] if len(suffix) != uuidSuffixLen { - return AnyUUID[P]{}, fmt.Errorf("typeid: invalid format: %q", s) - } - if vp, ok := any(&p).(VariablePrefixer); ok { - vp.ParsePrefix(pref) - } - if p.Prefix() != pref { - return AnyUUID[P]{}, fmt.Errorf("typeid: invalid prefix: %q", pref) + return AnyUUID{}, fmt.Errorf("typeid: invalid format: %q", s) } b, err := decodeBase32UUID(suffix) if err != nil { - return AnyUUID[P]{}, err + return AnyUUID{}, err } u := uuid.UUID(b) if u.Version() != 7 { - return AnyUUID[P]{}, ErrOnlyV7 + return AnyUUID{}, ErrOnlyV7 } - return AnyUUID[P]{val: u, prefix: p}, nil + return AnyUUID{val: u, prefix: pref}, nil } -func (id AnyUUID[P]) UUID() uuid.UUID { return id.val } -func (id AnyUUID[P]) Prefix() string { return id.prefix.Prefix() } -func (id AnyUUID[P]) Variant() P { return id.prefix } -func (id *AnyUUID[P]) SetPrefix(p P) { id.prefix = p } +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]) appendText(dst []byte) []byte { - return appendBase32UUID(dst, id.prefix.Prefix(), id.val) +func (id AnyUUID) appendText(dst []byte) []byte { + return appendBase32UUID(dst, id.prefix, id.val) } -func (id AnyUUID[P]) String() string { +func (id AnyUUID) String() string { var buf [64]byte return string(id.appendText(buf[:0])) } -func (id AnyUUID[P]) IsZero() bool { return id.val == uuid.UUID{} } +func (id AnyUUID) IsZero() bool { return id.val == uuid.UUID{} } -func (id AnyUUID[P]) MarshalText() ([]byte, error) { +func (id AnyUUID) MarshalText() ([]byte, error) { if id.IsZero() { return nil, ErrZeroUUID } return id.appendText(nil), nil } -func (id *AnyUUID[P]) UnmarshalText(data []byte) error { - parsed, err := ParseAnyUUID[P](string(data)) +func (id *AnyUUID) UnmarshalText(data []byte) error { + parsed, err := ParseAnyUUID(string(data)) if err != nil { return err } @@ -91,14 +82,14 @@ func (id *AnyUUID[P]) UnmarshalText(data []byte) error { return nil } -func (id AnyUUID[P]) Value() (driver.Value, error) { +func (id AnyUUID) Value() (driver.Value, error) { if id.IsZero() { return nil, ErrZeroUUID } return id.val.String(), nil } -func (id *AnyUUID[P]) Scan(src any) (err error) { +func (id *AnyUUID) Scan(src any) (err error) { var u uuid.UUID switch v := src.(type) { case string: @@ -127,7 +118,7 @@ func (id *AnyUUID[P]) Scan(src any) (err error) { } // GetTime extracts the millisecond-precision creation timestamp from the UUIDv7. -func (id AnyUUID[P]) GetTime() time.Time { +func (id AnyUUID) 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/int64_test.go b/int64_test.go index c70afea..16ba40f 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[typeid.AnyPrefix] `json:"id"` + ID typeid.AnyInt64 `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[typeid.AnyPrefix] `json:"id"` + ID typeid.AnyInt64 `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[typeid.AnyPrefix]("foo_" + suffix) + id, err := typeid.ParseAnyInt64("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[typeid.AnyPrefix]("org_" + suffix) + anyID, err := typeid.ParseAnyInt64("org_" + suffix) if err != nil { t.Fatal(err) } @@ -352,61 +352,3 @@ 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.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") - } -} - -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.Variant() != apiKeySandbox { - t.Errorf("Variant() = %d, want %d", parsed.Variant(), 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.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/typeid.go b/typeid.go index 2cbc370..468de56 100644 --- a/typeid.go +++ b/typeid.go @@ -11,23 +11,6 @@ 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 6b0b450..3d488df 100644 --- a/typeid_test.go +++ b/typeid_test.go @@ -26,62 +26,28 @@ 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{} _ fmt.Stringer = OrgID{} - _ fmt.Stringer = typeid.AnyUUID[typeid.AnyPrefix]{} - _ fmt.Stringer = typeid.AnyInt64[typeid.AnyPrefix]{} + _ fmt.Stringer = typeid.AnyUUID{} + _ fmt.Stringer = typeid.AnyInt64{} _ encoding.TextMarshaler = UserID{} _ encoding.TextMarshaler = OrgID{} - _ encoding.TextMarshaler = typeid.AnyUUID[typeid.AnyPrefix]{} - _ encoding.TextMarshaler = typeid.AnyInt64[typeid.AnyPrefix]{} + _ encoding.TextMarshaler = typeid.AnyUUID{} + _ encoding.TextMarshaler = typeid.AnyInt64{} _ encoding.TextUnmarshaler = (*UserID)(nil) _ encoding.TextUnmarshaler = (*OrgID)(nil) - _ encoding.TextUnmarshaler = (*typeid.AnyUUID[typeid.AnyPrefix])(nil) - _ encoding.TextUnmarshaler = (*typeid.AnyInt64[typeid.AnyPrefix])(nil) + _ encoding.TextUnmarshaler = (*typeid.AnyUUID)(nil) + _ encoding.TextUnmarshaler = (*typeid.AnyInt64)(nil) _ driver.Valuer = UserID{} _ driver.Valuer = OrgID{} - _ driver.Valuer = typeid.AnyUUID[typeid.AnyPrefix]{} - _ driver.Valuer = typeid.AnyInt64[typeid.AnyPrefix]{} + _ driver.Valuer = typeid.AnyUUID{} + _ driver.Valuer = typeid.AnyInt64{} _ sql.Scanner = (*UserID)(nil) _ sql.Scanner = (*OrgID)(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) - _ typeid.VariablePrefixer = (*apiKeyMode)(nil) + _ sql.Scanner = (*typeid.AnyUUID)(nil) + _ sql.Scanner = (*typeid.AnyInt64)(nil) ) func Example() { diff --git a/uuid.go b/uuid.go index f17dbd4..2b19277 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[AnyPrefix] { +func (id UUID[P]) Any() AnyUUID { var p P - return AnyUUID[AnyPrefix]{val: id.val, prefix: AnyPrefix(p.Prefix())} + return AnyUUID{val: id.val, prefix: p.Prefix()} } func (id *UUID[P]) Scan(src any) (err error) { diff --git a/uuid_test.go b/uuid_test.go index 886c399..9d32038 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[typeid.AnyPrefix] `json:"id"` + ID typeid.AnyUUID `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[typeid.AnyPrefix] `json:"id"` + ID typeid.AnyUUID `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[typeid.AnyPrefix]("user_" + suffix) + anyID, err := typeid.ParseAnyUUID("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[typeid.AnyPrefix]("foo_" + suffix) + id, err := typeid.ParseAnyUUID("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(typeid.AnyPrefix("bar")) + id.SetPrefix("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(typeid.AnyPrefix("user")) + id, err := typeid.NewAnyUUID("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(typeid.AnyPrefix("team"), raw) + id, err := typeid.AnyUUIDFrom("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(typeid.AnyPrefix("user"), v4) + _, err := typeid.AnyUUIDFrom("user", v4) if err == nil { t.Error("expected error for non-v7 UUID") } } func TestAnyUUID_String(t *testing.T) { - id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("user")) + id, _ := typeid.NewAnyUUID("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(typeid.AnyPrefix("apiKey")) + id, _ := typeid.NewAnyUUID("apiKey") if !strings.HasPrefix(id.String(), "apiKey_") { t.Fatalf("expected apiKey_ prefix, got %q", id.String()) } - id.SetPrefix(typeid.AnyPrefix("apiKeySandbox")) + id.SetPrefix("apiKeySandbox") if !strings.HasPrefix(id.String(), "apiKeySandbox_") { t.Errorf("expected apiKeySandbox_ prefix after SetPrefix, got %q", id.String()) } // Underlying UUID unchanged - id2, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) + id2, _ := typeid.NewAnyUUID("apiKey") raw := id2.UUID() - id2.SetPrefix(typeid.AnyPrefix("other")) + id2.SetPrefix("other") if id2.UUID() != raw { t.Error("SetPrefix should not change the UUID") } } func TestAnyUUID_MarshalText_RejectsZero(t *testing.T) { - var id typeid.AnyUUID[typeid.AnyPrefix] + var id typeid.AnyUUID _, 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(typeid.AnyPrefix("proj")) + original, _ := typeid.NewAnyUUID("proj") data, _ := original.MarshalText() - var parsed typeid.AnyUUID[typeid.AnyPrefix] + var parsed typeid.AnyUUID 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(typeid.AnyPrefix("apiKeySandbox")) + original, _ := typeid.NewAnyUUID("apiKeySandbox") data, _ := original.MarshalText() - var parsed typeid.AnyUUID[typeid.AnyPrefix] + var parsed typeid.AnyUUID 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(typeid.AnyPrefix("key")) + id, _ := typeid.NewAnyUUID("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[typeid.AnyPrefix] + var id typeid.AnyUUID _, 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(typeid.AnyPrefix("user")) + original, _ := typeid.NewAnyUUID("user") raw := original.UUID().String() - var scanned typeid.AnyUUID[typeid.AnyPrefix] + var scanned typeid.AnyUUID 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(typeid.AnyPrefix("user")) + original, _ := typeid.NewAnyUUID("user") raw := original.UUID() - var scanned typeid.AnyUUID[typeid.AnyPrefix] + var scanned typeid.AnyUUID 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[typeid.AnyPrefix] + var id typeid.AnyUUID 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(typeid.AnyPrefix("apiKey")) + id, _ := typeid.NewAnyUUID("apiKey") val, err := id.Value() if err != nil { t.Fatal(err) } - var scanned typeid.AnyUUID[typeid.AnyPrefix] + var scanned typeid.AnyUUID if err := scanned.Scan(val); err != nil { t.Fatal(err) } - scanned.SetPrefix(typeid.AnyPrefix("apiKeySandbox")) + scanned.SetPrefix("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(typeid.AnyPrefix("admin")) + any.SetPrefix("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(typeid.AnyPrefix("user")) + id, _ := typeid.NewAnyUUID("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[typeid.AnyPrefix] `json:"id"` - Name string `json:"name"` + ID typeid.AnyUUID `json:"id"` + Name string `json:"name"` } - id, _ := typeid.NewAnyUUID(typeid.AnyPrefix("apiKey")) + id, _ := typeid.NewAnyUUID("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(typeid.AnyPrefix("apiKeySandbox")) + id, _ := typeid.NewAnyUUID("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(typeid.AnyPrefix("apiKeySandbox")) + id, _ := typeid.NewAnyUUID("apiKeySandbox") s := id.String() b.ResetTimer() for b.Loop() { - typeid.ParseAnyUUID[typeid.AnyPrefix](s) //nolint:errcheck + typeid.ParseAnyUUID(s) //nolint:errcheck } } @@ -643,115 +643,3 @@ 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.Variant() != tt.wantMode { - t.Errorf("Variant() = %d, want %d", id.Variant(), 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.Variant() != apiKeySandbox { - t.Errorf("Variant() = %d, want %d", parsed.Variant(), 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.Variant() != apiKeyLive { - t.Fatalf("Variant() = %d, want %d", id.Variant(), apiKeyLive) - } - - id.SetPrefix(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") - } -} - -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.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") - } -} - -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.Variant() != apiKeyLive { - t.Errorf("Variant() = %d, want %d", id.Variant(), apiKeyLive) - } -}