From 170359d8409a0d07455715d369eb59535d58b8a4 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Wed, 2 Sep 2026 11:50:44 -0500 Subject: [PATCH] Fix distinct type row mapping --- README.rst | 4 ++ fiber_orm.nimble | 1 + src/fiber_orm/util.nim | 7 ++-- tests/test_distinct.nim | 84 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 tests/test_distinct.nim diff --git a/README.rst b/README.rst index 1997a45..b33333f 100644 --- a/README.rst +++ b/README.rst @@ -289,6 +289,10 @@ Nim Type Postgres Type SQLite Type `JsonNode`_ `jsonb`_ =============== ====================== ================= +Distinct types backed by a supported type are parsed through their backing +type and then converted to the distinct type. This also applies when the +distinct type is nested in an `Option`_ or `seq`. + .. [#f1] Note that this implies that all `NULL`-able fields should be typed as optional using `Option[fieldType]`. Conversely, any fields with non-optional types should also be constrained to be `NOT NULL` in diff --git a/fiber_orm.nimble b/fiber_orm.nimble index 482244c..ca55deb 100644 --- a/fiber_orm.nimble +++ b/fiber_orm.nimble @@ -18,6 +18,7 @@ requires "namespaced_logging >= 2.0.2" task unittest, "Runs the unit test suite.": exec "nim c -r --path:src tests/test_datetime" + exec "nim c -r --path:src tests/test_distinct" task integrationtest, "Runs the PostgreSQL integration test suite.": exec "nim c -r --path:src tests/test_datetime_postgres" diff --git a/src/fiber_orm/util.nim b/src/fiber_orm/util.nim index 0a1030e..8b89c7c 100644 --- a/src/fiber_orm/util.nim +++ b/src/fiber_orm/util.nim @@ -266,10 +266,9 @@ func createParseStmt*(t, value: NimNode): NimNode = else: error "Cannot parse column with unknown generic instance type: " & $t.getTypeInst elif t.typeKind == ntyDistinct: - result = quote do: - block: - let tmp: `t` = `value` - tmp + let baseType = t.getTypeImpl[0] + let parseStmt = createParseStmt(baseType, value) + result = quote do: `t`(`parseStmt`) elif t.typeKind == ntyRef: diff --git a/tests/test_distinct.nim b/tests/test_distinct.nim new file mode 100644 index 0000000..f4d4d12 --- /dev/null +++ b/tests/test_distinct.nim @@ -0,0 +1,84 @@ +import std/[options, times, unittest] + +import uuids + +import fiber_orm/util + + +type + StringId = distinct string + NestedStringId = distinct StringId + UuidId = distinct UUID + Count = distinct int + Ratio = distinct float + Enabled = distinct bool + + State = enum + pending + complete + + DistinctState = distinct State + + DistinctModel = object + id: StringId + nestedId: NestedStringId + userId: UuidId + count: Count + ratio: Ratio + enabled: Enabled + state: DistinctState + optionalId: Option[StringId] + absentId: Option[StringId] + relatedIds: seq[StringId] + occurredAt: DateTime + + +suite "distinct type row mapping": + + test "parses each distinct type through its backing type": + let model = rowToModel(DistinctModel, @[ + "item_123", + "nested_123", + "07e268ed-a3c1-4952-bc45-778b3000b76c", + "42", + "1.25", + "true", + "complete", + "optional_123", + "", + "{related_1,related_2}", + "2026-08-26 14:32:10.123457+00", + ]) + + check: + string(model.id) == "item_123" + string(StringId(model.nestedId)) == "nested_123" + UUID(model.userId) == + parseUUID("07e268ed-a3c1-4952-bc45-778b3000b76c") + int(model.count) == 42 + float(model.ratio) == 1.25 + bool(model.enabled) + State(model.state) == complete + model.optionalId.isSome + string(model.optionalId.get) == "optional_123" + model.absentId.isNone + model.relatedIds.len == 2 + string(model.relatedIds[0]) == "related_1" + string(model.relatedIds[1]) == "related_2" + model.occurredAt.nanosecond == 123_457_000 + + test "preserves backing-type parse failures": + expect ValueError: + discard rowToModel(DistinctModel, @[ + "item_123", + "nested_123", + "not-a-uuid", + "42", + "1.25", + "true", + "complete", + "", + "", + "{}", + "2026-08-26 14:32:10+00", + ])