From baea715bea4e3492d365796e0d6c81caa43c92eb Mon Sep 17 00:00:00 2001
From: Sergei Zharinov <zharinov@users.noreply.github.com>
Date: Thu, 28 Nov 2024 14:07:36 -0300
Subject: [PATCH] feat: Wrap callback returning Promise for `Result` utility
 (#32788)

---
 lib/util/result.spec.ts | 15 +++++++++++++--
 lib/util/result.ts      |  9 +++++++++
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/lib/util/result.spec.ts b/lib/util/result.spec.ts
index a035fce181..4a22eec2ee 100644
--- a/lib/util/result.spec.ts
+++ b/lib/util/result.spec.ts
@@ -31,18 +31,29 @@ describe('util/result', () => {
     });
 
     describe('Wrapping', () => {
-      it('wraps callback', () => {
+      it('wraps callback returning value', () => {
         const res = Result.wrap(() => 42);
         expect(res).toEqual(Result.ok(42));
       });
 
-      it('handles callback error', () => {
+      it('handles throw in callback', () => {
         const res = Result.wrap(() => {
           throw 'oops';
         });
         expect(res).toEqual(Result.err('oops'));
       });
 
+      it('wraps callback returning promise', () => {
+        const res = Result.wrap(() => Promise.resolve(42));
+        expect(res).toEqual(AsyncResult.ok(42));
+      });
+
+      it('wraps callback returning failed promise', () => {
+        const err = new Error('unknown');
+        const res = Result.wrap(() => Promise.reject(err));
+        expect(res).toEqual(AsyncResult.err(err));
+      });
+
       it('wraps nullable callback', () => {
         const res: Result<number, 'oops'> = Result.wrapNullable(
           (): number | null => 42,
diff --git a/lib/util/result.ts b/lib/util/result.ts
index af699f33ea..86f48acdf2 100644
--- a/lib/util/result.ts
+++ b/lib/util/result.ts
@@ -142,6 +142,9 @@ export class Result<T extends Val, E extends Val = Error> {
   static wrap<T extends Val, E extends Val = Error>(
     callback: () => RawValue<T>,
   ): Result<T, E>;
+  static wrap<T extends Val, E extends Val = Error>(
+    callback: () => Promise<RawValue<T>>,
+  ): AsyncResult<T, E>;
   static wrap<T extends Val, E extends Val = Error, EE extends Val = never>(
     promise: Promise<Result<T, EE>>,
   ): AsyncResult<T, E | EE>;
@@ -157,6 +160,7 @@ export class Result<T extends Val, E extends Val = Error> {
     input:
       | SafeParseReturnType<Input, T>
       | (() => RawValue<T>)
+      | (() => Promise<RawValue<T>>)
       | Promise<Result<T, EE>>
       | Promise<RawValue<T>>,
   ): Result<T, ZodError<Input>> | Result<T, E | EE> | AsyncResult<T, E | EE> {
@@ -170,6 +174,11 @@ export class Result<T extends Val, E extends Val = Error> {
 
     try {
       const result = input();
+
+      if (result instanceof Promise) {
+        return AsyncResult.wrap(result);
+      }
+
       return Result.ok(result);
     } catch (error) {
       return Result.err(error);
-- 
GitLab