Repository: Quick/Nimble (swift)

Issue:
Suggestion: throwing counterpart to `fail(message`
a common pattern in swift tests is failing after checking some condition in a if/guard
```swift
guard let case .someCase(associatedValue) = someEnum else {
  fail("some explanation")
  return
}
```

it could be convenient to have a `require` counterpart to the `fail` function that throws an error.

Of coarse, users could always throw their own errors, but for users aren't used to doing so, it involves a lot of considerations.

it is possible to achieve this behavior with the existing `require` DSL, but it looks a bit wierd:
```swift
try require(false).verify(false, FailureMessage(stringValue: description), nil)
```
or as a utility:
```swift
private func failWithError(
  file: FileString = #filePath, line: UInt = #line,
  _ description: String
) throws -> Never {
  try require(file: file, line: line, false).verify(false, FailureMessage(stringValue: description),
                                                    nil)
  throw SomeErrorSayingWeCantGetHere
}
```

this would be simplified greatly with the access to the `RequireError` that does nice things.
it could be simple as adding
```swift
public func throwingFail(_ message: String, location: SourceLocation) throws -> Never {
    let handler = NimbleEnvironment.activeInstance.assertionHandler
    handler.assert(false, message: FailureMessage(stringValue: message), location: location)
    throw RequireError(message: message, location: location)
}
```
and the `,file, line)` counterparts to `DSL+Require`

sorry in advance if there is already something simpler that could be used as a throwing `fail`

Pull request description:
Fixes https://github.com/Quick/Nimble/issues/1161

The PR should summarize what was changed and why. Here are some questions to
help you if you're not sure:

- What behavior was changed?
- What code was refactored / updated to support this change?
- What issues are related to this PR? Or why was this change introduced?

Checklist - While not every PR needs it, new features should consider this list:

- [x] Does this have tests?
- [x] Does this have documentation?
- [ ] Does this break the public API (Requires major version bump)?
- [x] Is this a new feature (Requires minor version bump)?

Patch:
diff --git a/Sources/Nimble/DSL+Require.swift b/Sources/Nimble/DSL+Require.swift
index 4df5c5b4d..bd73f72f6 100644
--- a/Sources/Nimble/DSL+Require.swift
+++ b/Sources/Nimble/DSL+Require.swift
@@ -289,3 +289,17 @@ public func unwrapa<T>(fileID: String = #fileID, file: FileString = #filePath, l
 public func unwrapa<T>(fileID: String = #fileID, file: FileString = #filePath, line: UInt = #line, column: UInt = #column, customError: Error? = nil, description: String? = nil, _ expression: @autoclosure () -> (() async throws -> T?)) async throws -> T {
     try await requirea(fileID: fileID, file: file, line: line, column: column, customError: customError, expression()).toNot(beNil(), description: description)
 }
+
+/// Always fails the test and throw an error to prevent further test execution.
+///
+/// - Parameter message: A custom message to use in place of the default one.
+/// - Parameter customError: A custom error to throw in place of a ``RequireError``.
+public func requireFail(_ message: String? = nil, customError: Error? = nil, fileID: String = #fileID, filePath: FileString = #filePath, line: UInt = #line, column: UInt = #column) throws {
+    let location = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column)
+    let handler = NimbleEnvironment.activeInstance.assertionHandler
+
+    let msg = message ?? "requireFail() always fails"
+    handler.assert(false, message: FailureMessage(stringValue: msg), location: location)
+
+    throw customError ?? RequireError(message: msg, location: location)
+}


Test patch:
diff --git a/Tests/NimbleTests/DSLTest.swift b/Tests/NimbleTests/DSLTest.swift
index 1c828686e..12d3f97a0 100644
--- a/Tests/NimbleTests/DSLTest.swift
+++ b/Tests/NimbleTests/DSLTest.swift
@@ -219,4 +219,32 @@ final class DSLTest: XCTestCase {
             try await unwrapa(description: "Other Message", await asyncOptional(nil))
         }
     }
+
+    func testRequireFail() throws {
+        struct MyCustomError: Error {}
+
+        failsWithErrorMessage("requireFail() always fails") {
+            do {
+                try requireFail()
+            } catch {
+                expect(error as? RequireError).toNot(beNil())
+            }
+        }
+
+        failsWithErrorMessage("Custom error message") {
+            do {
+                try requireFail("Custom error message")
+            } catch {
+                expect(error as? RequireError).toNot(beNil())
+            }
+        }
+
+        failsWithErrorMessage("Custom message with custom error") {
+            do {
+                try requireFail("Custom message with custom error", customError: MyCustomError())
+            } catch {
+                expect(error as? MyCustomError).toNot(beNil())
+            }
+        }
+    }
 }