From fe37a82d0495a8b59b80677dd42d0d94ffafdfb4 Mon Sep 17 00:00:00 2001 From: Stefan Majewsky Date: Thu, 17 Sep 2026 15:50:45 +0200 Subject: gsql: add NoneIfNoRows --- CHANGELOG.md | 7 +++++++ go.mod | 2 +- gsql/gsql.go | 18 ++++++++++++++++++ gsql/gsql_test.go | 29 +++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 gsql/gsql_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index f259e37..a37cb78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ SPDX-FileCopyrightText: 2026 Stefan Majewsky SPDX-License-Identifier: Apache-2.0 --> +# v1.16.0 (TBD) + +Changes: + +- gsql: Add NoneIfNoRows. + This replaces "OrNone" variants of methods in Oblast, e.g. SelectOne() and then also SelectOneOrNone(). + # v1.15.0 (2026-09-08) Changes: diff --git a/go.mod b/go.mod index 0cd2a2d..9bb7b69 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ module go.xyrillian.de/gg -// TODO for 1.27: use sql.ConvertAssign for gg/option; deprecate gg/options and offer methods on Option instead; make jsonmatch.Pointer into an alias of jsontext.Pointer +// TODO for 1.27: use sql.ConvertAssign for gg/option; deprecate gg/options and offer methods on Option instead; make jsonmatch.Pointer into an alias of jsontext.Pointer; replace json.{Unm,M}arshaler with json/v2.{Unm,M}arshaler{From,To} go 1.26 diff --git a/gsql/gsql.go b/gsql/gsql.go index 7df830c..e7e2aa3 100644 --- a/gsql/gsql.go +++ b/gsql/gsql.go @@ -14,6 +14,9 @@ package gsql import ( "context" "database/sql" + "errors" + + . "go.xyrillian.de/gg/option" ) // ConnectionHandle extends [Handle] with methods that make sense for handles referring to entire connections or connection pools, but not e.g. to transactions. @@ -76,3 +79,18 @@ type Rows interface { Next() bool Scan(slots ...any) error } + +// NoneIfNoRows wraps any call returning a record from a DB that may return [sql.ErrNoRows], +// and converts that error into a [None] in the value position instead. +// +// [None]: https://pkg.go.dev/go.xyrillian.de/gg/option#None +func NoneIfNoRows[T any](value T, err error) (Option[T], error) { + switch { + case err == nil: + return Some(value), nil + case errors.Is(err, sql.ErrNoRows): + return None[T](), nil + default: + return None[T](), err + } +} diff --git a/gsql/gsql_test.go b/gsql/gsql_test.go new file mode 100644 index 0000000..acf0bbf --- /dev/null +++ b/gsql/gsql_test.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2026 Stefan Majewsky +// SPDX-License-Identifier: Apache-2.0 + +package gsql_test + +import ( + "database/sql" + "errors" + "testing" + + "go.xyrillian.de/gg/assert" + "go.xyrillian.de/gg/gsql" + . "go.xyrillian.de/gg/option" +) + +func TestNoneIfNoRows(t *testing.T) { + x, err := gsql.NoneIfNoRows(42, nil) + if assert.ErrEqual(t, err, nil) { + assert.Equal(t, x, Some(42)) + } + + x, err = gsql.NoneIfNoRows(0, sql.ErrNoRows) + if assert.ErrEqual(t, err, nil) { + assert.Equal(t, x, None[int]()) + } + + _, err = gsql.NoneIfNoRows(0, errors.New("kaboom")) + assert.ErrEqual(t, err, "kaboom") +} -- cgit v1.3.1