a303e025b2
Replace the UUID wrapper with storage-specific KSUID types that share GraphQL string transit but use distinct SQL Scan/Value encodings. Co-authored-by: Cursor <cursoragent@cursor.com>
77 lines
2.0 KiB
Go
77 lines
2.0 KiB
Go
package dbx
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/segmentio/ksuid"
|
|
)
|
|
|
|
// BinaryKSUID wraps segmentio/ksuid for binary-column storage (BINARY(20), BYTEA).
|
|
// SQL Scan/Value use the raw 20-byte encoding. GraphQL transit uses the shared
|
|
// string JSON transport (see ksuid_gql.go).
|
|
type BinaryKSUID struct {
|
|
ksuid.KSUID
|
|
}
|
|
|
|
// NewBinaryKSUID generates a new BinaryKSUID.
|
|
func NewBinaryKSUID() BinaryKSUID {
|
|
return BinaryKSUID{KSUID: ksuid.New()}
|
|
}
|
|
|
|
// ParseBinaryKSUID parses a BinaryKSUID from its raw 20-byte form.
|
|
func ParseBinaryKSUID(b []byte) (BinaryKSUID, error) {
|
|
id, err := ksuid.FromBytes(b)
|
|
if err != nil {
|
|
return BinaryKSUID{}, err
|
|
}
|
|
return BinaryKSUID{KSUID: id}, nil
|
|
}
|
|
|
|
// AsStringKSUID returns a StringKSUID view of the same identifier.
|
|
func (b BinaryKSUID) AsStringKSUID() StringKSUID {
|
|
return StringKSUID{KSUID: b.KSUID}
|
|
}
|
|
|
|
// Scan implements sql.Scanner for binary-backed KSUID columns.
|
|
func (b *BinaryKSUID) Scan(src any) error {
|
|
switch v := src.(type) {
|
|
case nil:
|
|
*b = BinaryKSUID{}
|
|
return nil
|
|
case []byte:
|
|
if len(v) == ksuidStringLength {
|
|
return fmt.Errorf("BinaryKSUID.Scan: string-encoded KSUID (%d bytes); use StringKSUID", len(v))
|
|
}
|
|
id, err := ksuid.FromBytes(v)
|
|
if err != nil {
|
|
return fmt.Errorf("BinaryKSUID.Scan: %w", err)
|
|
}
|
|
*b = BinaryKSUID{KSUID: id}
|
|
return nil
|
|
case string:
|
|
return fmt.Errorf("BinaryKSUID.Scan: string value %q; use StringKSUID or store raw bytes", v)
|
|
default:
|
|
return fmt.Errorf("BinaryKSUID.Scan: unable to scan type %T", v)
|
|
}
|
|
}
|
|
|
|
// Value implements driver.Valuer for binary-backed KSUID columns.
|
|
func (b BinaryKSUID) Value() (driver.Value, error) {
|
|
if b.IsNil() {
|
|
return nil, nil
|
|
}
|
|
return b.Bytes(), nil
|
|
}
|
|
|
|
// UnmarshalGQL implements the graphql.Unmarshaler interface.
|
|
func (b *BinaryKSUID) UnmarshalGQL(value any) error {
|
|
return unmarshalKSUIDFromGQL(value, b.UnmarshalText)
|
|
}
|
|
|
|
// MarshalGQL implements the graphql.Marshaler interface.
|
|
func (b BinaryKSUID) MarshalGQL(w io.Writer) {
|
|
marshalKSUIDToGQL(w, b.KSUID)
|
|
}
|