From 2f46f64b0e026c7a44e74163854dd9c85f6e2870 Mon Sep 17 00:00:00 2001 From: Justin Bishop Date: Wed, 30 Apr 2025 22:23:25 -0700 Subject: [PATCH 1/2] add an async variant to Catching.catch --- Sources/ErrorKit/Catching.swift | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Sources/ErrorKit/Catching.swift b/Sources/ErrorKit/Catching.swift index 5e4a50a..d230d39 100644 --- a/Sources/ErrorKit/Catching.swift +++ b/Sources/ErrorKit/Catching.swift @@ -139,4 +139,49 @@ extension Catching { throw Self.caught(error) } } + + /// Executes an async throwing operation and automatically wraps any thrown errors into this error type's `caught` case, + /// while passing through the operation's return value on success. Great for functions using typed throws. + /// + /// # Overview + /// This function provides a convenient way to: + /// - Execute async throwing operations + /// - Automatically wrap any errors into the current error type + /// - Pass through return values from the wrapped code + /// - Maintain type safety with typed throws + /// + /// # Example + /// ```swift + /// struct ProfileRepository { + /// func loadProfile(id: String) throws(ProfileError) { + /// // Regular error throwing for validation + /// guard id.isValidFormat else { + /// throw ProfileError.validationFailed(field: "id") + /// } + /// + /// // Automatically wrap any database or file errors while handling return value + /// let userData = try await ProfileError.catch { + /// let user = try await database.loadUser(id) + /// let settings = try await fileSystem.readUserSettings(user.settingsPath) + /// return UserProfile(user: user, settings: settings) + /// } + /// + /// // Use the loaded data + /// self.currentProfile = userData + /// } + /// } + /// ``` + /// + /// - Parameter operation: The async throwing operation to execute. + /// - Returns: The value returned by the operation if successful. + /// - Throws: An instance of `Self` with the original error wrapped in the `caught` case. + public static func `catch`( + _ operation: () async throws -> ReturnType + ) async throws(Self) -> ReturnType { + do { + return try await operation() + } catch { + throw Self.caught(error) + } + } } From a978dd55b33a1fd8493c08fe18bd4c1ac950cd1c Mon Sep 17 00:00:00 2001 From: Justin Bishop Date: Wed, 30 Apr 2025 22:30:35 -0700 Subject: [PATCH 2/2] if error caught from catch is already Self, just throw it directly rather than wrapping in a caught enum. --- Sources/ErrorKit/Catching.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/ErrorKit/Catching.swift b/Sources/ErrorKit/Catching.swift index d230d39..5712570 100644 --- a/Sources/ErrorKit/Catching.swift +++ b/Sources/ErrorKit/Catching.swift @@ -135,6 +135,8 @@ extension Catching { ) throws(Self) -> ReturnType { do { return try operation() + } catch let error as Self { + throw error } catch { throw Self.caught(error) } @@ -180,6 +182,8 @@ extension Catching { ) async throws(Self) -> ReturnType { do { return try await operation() + } catch let error as Self { + throw error } catch { throw Self.caught(error) }