mirror of
https://github.com/hanzoai/orm.git
synced 2026-10-30 15:01:22 +00:00
A query knows how to fetch T. Turning those rows into the shape a caller
actually wants was the caller's loop every time — fetch, allocate, range,
transform — written once per call site and wrong in the same place each time it
was wrong: an empty result that still runs the transform once, or returns an
empty slice where the rest of the package returns nil.
Map and OneMap put that on the query. They are generic methods (Go 1.27): the
receiver already carries T, and the method declares U itself, which is the thing
1.27 added. Inference does the rest — the closure's return type IS U, so a call
site names no type arguments at all:
summaries, err := orm.Select[User](db, "users").Map(ctx, func(u User) Summary {
return Summary{ID: u.ID, Label: u.Email}
})
Empty is nil and the transform does not run, in all three. That is the branch
worth pinning: a transform invoked once on a zero value reads as one result to
everything that ranges over the answer, and ModelQuery.Map was the one of the
three the suite could not reach — generic_methods_test.go is an external test
and cannot build a ModelQuery, so its test sits inside the package.
go.mod names 1.27 because generic methods require it; the compiler is explicit
about that and the tree builds clean on it.
Co-authored-by: Hanzo Dev <[email protected]>
32 lines
1.1 KiB
Go
32 lines
1.1 KiB
Go
package orm
|
|
|
|
import "testing"
|
|
|
|
// ModelQuery.Map is the third of the three generic methods and the only one the
|
|
// suite did not reach: Typed.Map and Typed.OneMap are covered in
|
|
// generic_methods_test.go, which is an external test and therefore cannot build
|
|
// a ModelQuery — newMockDB is unexported. So this one sits inside the package.
|
|
//
|
|
// What it pins is the EMPTY case, which is the branch a transform is most
|
|
// likely to get wrong: Map returns nil rather than an empty slice, and must not
|
|
// call fn at all. A transform invoked once on a zero value would look like a
|
|
// single result to every caller that ranges over the answer.
|
|
func TestModelQueryMapGenericMethod(t *testing.T) {
|
|
q := New[PaymentIntent](newMockDB()).Query()
|
|
|
|
called := 0
|
|
out, err := q.Map(nil, func(p *PaymentIntent) string {
|
|
called++
|
|
return p.Id()
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Map on an empty result: %v", err)
|
|
}
|
|
if out != nil {
|
|
t.Errorf("empty result must map to nil, got %#v", out)
|
|
}
|
|
if called != 0 {
|
|
t.Errorf("fn ran %d times on an empty result; it must not run at all", called)
|
|
}
|
|
}
|