feat: add StringKSUID and BinaryKSUID types
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>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package dbx
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
// StringKSUID wraps segmentio/ksuid for text-column storage (VARCHAR, TEXT).
|
||||
// SQL Scan/Value use the base62 string encoding. GraphQL transit uses the
|
||||
// shared string JSON transport (see ksuid_gql.go).
|
||||
type StringKSUID struct {
|
||||
ksuid.KSUID
|
||||
}
|
||||
|
||||
// NewStringKSUID generates a new StringKSUID.
|
||||
func NewStringKSUID() StringKSUID {
|
||||
return StringKSUID{KSUID: ksuid.New()}
|
||||
}
|
||||
|
||||
// ParseStringKSUID parses a StringKSUID from its base62 string form.
|
||||
func ParseStringKSUID(s string) (StringKSUID, error) {
|
||||
id, err := ksuid.Parse(s)
|
||||
if err != nil {
|
||||
return StringKSUID{}, err
|
||||
}
|
||||
return StringKSUID{KSUID: id}, nil
|
||||
}
|
||||
|
||||
// AsBinaryKSUID returns a BinaryKSUID view of the same identifier.
|
||||
func (s StringKSUID) AsBinaryKSUID() BinaryKSUID {
|
||||
return BinaryKSUID{KSUID: s.KSUID}
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner for string-backed KSUID columns.
|
||||
func (s *StringKSUID) Scan(src any) error {
|
||||
switch v := src.(type) {
|
||||
case nil:
|
||||
*s = StringKSUID{}
|
||||
return nil
|
||||
case string:
|
||||
id, err := ksuid.Parse(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("StringKSUID.Scan: %w", err)
|
||||
}
|
||||
*s = StringKSUID{KSUID: id}
|
||||
return nil
|
||||
case []byte:
|
||||
if len(v) == ksuidBinaryLength {
|
||||
return fmt.Errorf("StringKSUID.Scan: binary KSUID payload (%d bytes); use BinaryKSUID", len(v))
|
||||
}
|
||||
id, err := ksuid.Parse(string(v))
|
||||
if err != nil {
|
||||
return fmt.Errorf("StringKSUID.Scan: %w", err)
|
||||
}
|
||||
*s = StringKSUID{KSUID: id}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("StringKSUID.Scan: unable to scan type %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer for string-backed KSUID columns.
|
||||
func (s StringKSUID) Value() (driver.Value, error) {
|
||||
if s.IsNil() {
|
||||
return nil, nil
|
||||
}
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
// UnmarshalGQL implements the graphql.Unmarshaler interface.
|
||||
func (s *StringKSUID) UnmarshalGQL(value any) error {
|
||||
return unmarshalKSUIDFromGQL(value, s.UnmarshalText)
|
||||
}
|
||||
|
||||
// MarshalGQL implements the graphql.Marshaler interface.
|
||||
func (s StringKSUID) MarshalGQL(w io.Writer) {
|
||||
marshalKSUIDToGQL(w, s.KSUID)
|
||||
}
|
||||
Reference in New Issue
Block a user