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 a string // scalar (base62 KSUID). type StringKSUID struct { ksuid.KSUID } // NilStringKSUID is the zero/nil StringKSUID value. var NilStringKSUID = StringKSUID{KSUID: ksuid.Nil} // Equal reports whether two StringKSUID values represent the same identifier, // treating two nil values as equal. Implements [ApplyInterface]. func (s StringKSUID) Equal(rh StringKSUID) bool { return equalKSUID(s.KSUID, rh.KSUID) } // IsZero reports whether s is nil. Implements [ApplyInterface]. func (s StringKSUID) IsZero() bool { return s.IsNil() } // 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) }