From a712ccc38f0da8daebc9c10ef5904ef1fff95360 Mon Sep 17 00:00:00 2001 From: Elijah Duffy Date: Mon, 29 Jun 2026 18:47:18 -0700 Subject: [PATCH] refactor(ksuid): replace BinExpr with top-level expr helpers Adds StringKSUIDExpr and BinaryKSUIDExpr for use with ExprValues, plus Expr() methods on both types. Removes ExprStringKSUIDs and BinExpr. Co-authored-by: Cursor --- CHANGELOG.md | 2 +- README.md | 2 +- doc.go | 6 ++++-- jet_expr.go | 19 +++++++++++++------ jet_expr_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++++ ksuid_binary.go | 14 +++++--------- ksuid_parse_test.go | 16 ---------------- ksuid_string.go | 5 +++++ 8 files changed, 75 insertions(+), 35 deletions(-) create mode 100644 jet_expr_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a0690e2..34d1816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - `Query`, `MustQuery`, and `UpdateOne` helpers (+ Context variants). - `WithTxValue` for transactional functions that return a value. - `QueryCount`, `BuildQueryCountFn`, and `CountResult` for pagination counts. -- `BinaryKSUID.BinExpr`, `ExprStringKSUIDs`, and flexible `Parse*Any` KSUID parsers. +- `StringKSUIDExpr`, `BinaryKSUIDExpr`, and flexible `Parse*Any` KSUID parsers. - `MarshalUint64` and `UnmarshalUint64` for JS-safe GraphQL uint64 scalars. - Context-aware variants for all query and mutation helpers. - `Delete`, `DeleteAffected`, `WithTx`, `ContainsCol`, and `CurrentDialect()`. diff --git a/README.md b/README.md index bfe555a..7b7ddc3 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ For Postgres, use `dbx.DialectPostgres` and blank-import `dbxp` instead of `dbxm | Query | `Fetch`, `MustFetch`, `FetchOne`, `MustFetchOne`, `Query`, `MustQuery` (+ `*Context` variants) | | Mutations | `Insert`, `InsertReturning`, `Update`, `UpdateAffected`, `UpdateOne`, `UpdateReturning`, `Delete`, `DeleteAffected` (+ `*Context` variants) | | Transactions | `WithTx`, `WithTxValue` | -| Columns | `NormalCols`, `ContainsCol`, `ExprValues`, `ExprStringers`, `ExprStringKSUIDs`, `QueryCount`, `BuildQueryCountFn` | +| Columns | `NormalCols`, `ContainsCol`, `ExprValues`, `ExprStringers`, `StringKSUIDExpr`, `BinaryKSUIDExpr`, `QueryCount`, `BuildQueryCountFn` | | Partial update | `ApplyPtr`, `ApplyComplexPtr`, `ApplyInterfacePtr`, `ApplyVal` | | Pointers | `Ptr`, `Val`, `NowPtr`, `TrimPtr`, `TrimPtrToNil`, `IsZero` | | Types | `StringKSUID`, `BinaryKSUID`, `JSONB` | diff --git a/doc.go b/doc.go index 26e43de..25df5d8 100644 --- a/doc.go +++ b/doc.go @@ -16,7 +16,8 @@ // # Jet column utilities // // Dialect-neutral type aliases ([Column], [ColumnList]) and helpers for column -// lists ([NormalCols], [ContainsCol]) and expression building ([ExprValues]). +// lists ([NormalCols], [ContainsCol]) and expression building ([ExprValues], +// [StringKSUIDExpr], [BinaryKSUIDExpr]). // [QueryCount] and [BuildQueryCountFn] support pagination total counts. // // Partial-update helpers ([ApplyPtr], [ApplyVal]) track changed fields for @@ -26,7 +27,8 @@ // // [StringKSUID] and [BinaryKSUID] wrap segmentio/ksuid with storage-specific // SQL encoding. Both implement [ApplyInterface] via Equal and IsZero for use -// with [ApplyInterfacePtr]. Both share identical GraphQL string scalar transit. +// with [ApplyInterfacePtr]. Expr returns a Jet expression via [StringKSUIDExpr] +// or [BinaryKSUIDExpr]. Both share identical GraphQL string scalar transit. // Choose StringKSUID for text columns (VARCHAR, TEXT); choose BinaryKSUID for // binary columns (BINARY(20), BYTEA). [ParseStringKSUIDAny] and // [ParseBinaryKSUIDAny] accept flexible input for API boundaries. diff --git a/jet_expr.go b/jet_expr.go index 8d85d1a..373b184 100644 --- a/jet_expr.go +++ b/jet_expr.go @@ -25,11 +25,18 @@ func ExprStringers(values []fmt.Stringer) []Expression { return expressions } -// ExprStringKSUIDs converts StringKSUID pointers to Jet string expressions. -func ExprStringKSUIDs(ids []*StringKSUID) []Expression { - expressions := make([]Expression, len(ids)) - for i, id := range ids { - expressions[i] = mysql.String(id.String()) +// StringKSUIDExpr returns a Jet expression for a string-encoded KSUID column. +func StringKSUIDExpr(id StringKSUID) Expression { + if id.IsNil() { + return mysql.StringExp(mysql.NULL) } - return expressions + return mysql.String(id.String()) +} + +// BinaryKSUIDExpr returns a Jet expression for a binary-encoded KSUID column. +func BinaryKSUIDExpr(id BinaryKSUID) Expression { + if id.IsNil() { + return mysql.StringExp(mysql.NULL) + } + return mysql.String(string(id.Bytes())) } diff --git a/jet_expr_test.go b/jet_expr_test.go new file mode 100644 index 0000000..5500168 --- /dev/null +++ b/jet_expr_test.go @@ -0,0 +1,46 @@ +package dbx + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStringKSUIDExpr(t *testing.T) { + id := NewStringKSUID() + expr := StringKSUIDExpr(id) + require.NotNil(t, expr) + assert.Equal(t, expr, id.Expr()) + + nilExpr := StringKSUIDExpr(NilStringKSUID) + require.NotNil(t, nilExpr) + assert.Equal(t, nilExpr, NilStringKSUID.Expr()) +} + +func TestBinaryKSUIDExpr(t *testing.T) { + id := NewBinaryKSUID() + expr := BinaryKSUIDExpr(id) + require.NotNil(t, expr) + assert.Equal(t, expr, id.Expr()) + + nilExpr := BinaryKSUIDExpr(NilBinaryKSUID) + require.NotNil(t, nilExpr) + assert.Equal(t, nilExpr, NilBinaryKSUID.Expr()) +} + +func TestExprValues_StringKSUID(t *testing.T) { + a := NewStringKSUID() + b := NewStringKSUID() + exprs := ExprValues([]*StringKSUID{&a, &b}, func(id *StringKSUID) Expression { + return id.Expr() + }) + require.Len(t, exprs, 2) +} + +func TestExprValues_BinaryKSUID(t *testing.T) { + a := NewBinaryKSUID() + exprs := ExprValues([]BinaryKSUID{a}, BinaryKSUIDExpr) + require.Len(t, exprs, 1) + assert.NotNil(t, exprs[0]) +} diff --git a/ksuid_binary.go b/ksuid_binary.go index 31383eb..e51af62 100644 --- a/ksuid_binary.go +++ b/ksuid_binary.go @@ -5,7 +5,6 @@ import ( "fmt" "io" - "github.com/go-jet/jet/v2/mysql" "github.com/segmentio/ksuid" ) @@ -44,19 +43,16 @@ func ParseBinaryKSUID(b []byte) (BinaryKSUID, error) { return BinaryKSUID{KSUID: id}, nil } -// BinExpr returns a Jet string expression for binary-encoded IN clauses. -func (b BinaryKSUID) BinExpr() mysql.StringExpression { - if b.IsNil() { - return mysql.StringExp(mysql.NULL) - } - return mysql.String(string(b.Bytes())) -} - // AsStringKSUID returns a StringKSUID view of the same identifier. func (b BinaryKSUID) AsStringKSUID() StringKSUID { return StringKSUID{KSUID: b.KSUID} } +// Expr returns a Jet expression for this BinaryKSUID. See [BinaryKSUIDExpr]. +func (b BinaryKSUID) Expr() Expression { + return BinaryKSUIDExpr(b) +} + // Scan implements sql.Scanner for binary-backed KSUID columns. func (b *BinaryKSUID) Scan(src any) error { switch v := src.(type) { diff --git a/ksuid_parse_test.go b/ksuid_parse_test.go index 6428676..743068e 100644 --- a/ksuid_parse_test.go +++ b/ksuid_parse_test.go @@ -52,19 +52,3 @@ func TestParseStringKSUIDAny_InvalidType(t *testing.T) { _, err := ParseStringKSUIDAny(123) assert.Error(t, err) } - -func TestBinaryKSUID_BinExpr(t *testing.T) { - id := NewBinaryKSUID() - expr := id.BinExpr() - require.NotNil(t, expr) - - nilExpr := NilBinaryKSUID.BinExpr() - require.NotNil(t, nilExpr) -} - -func TestExprStringKSUIDs(t *testing.T) { - a := NewStringKSUID() - b := NewStringKSUID() - exprs := ExprStringKSUIDs([]*StringKSUID{&a, &b}) - require.Len(t, exprs, 2) -} diff --git a/ksuid_string.go b/ksuid_string.go index 6278e50..a1b7cf8 100644 --- a/ksuid_string.go +++ b/ksuid_string.go @@ -48,6 +48,11 @@ func (s StringKSUID) AsBinaryKSUID() BinaryKSUID { return BinaryKSUID{KSUID: s.KSUID} } +// Expr returns a Jet expression for this StringKSUID. See [StringKSUIDExpr]. +func (s StringKSUID) Expr() Expression { + return StringKSUIDExpr(s) +} + // Scan implements sql.Scanner for string-backed KSUID columns. func (s *StringKSUID) Scan(src any) error { switch v := src.(type) {