Skip to content

Commit 2cfbb33

Browse files
committed
Expand require extensions with features for collections and type casting, additional features for Bool
1 parent e21927f commit 2cfbb33

File tree

6 files changed

+180
-10
lines changed

6 files changed

+180
-10
lines changed

Sources/BuddyFoundation/Foundation/Bool+Require.swift

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
public extension Bool {
2+
/// Throws an error if the bool value is `false`.
3+
/// - Parameter error: The error to throw if the value is `false`.
4+
/// - Returns: `true` if the value was `true` (discardable result).
5+
@discardableResult
6+
func require<E: Error>(_ error: E) throws(E) -> Self {
7+
guard self else { throw error }
8+
return true
9+
}
10+
11+
/// Throws an error if the bool value is `false`.
12+
/// - Parameter error: The error to throw if the value is `false`.
13+
/// - Returns: `true` if the value was true (discardable result).
14+
@discardableResult
15+
func requireTrue<E: Error>(_ error: E) throws(E) -> Self {
16+
guard self else { throw error }
17+
return true
18+
}
19+
20+
/// Throws an error if the bool value is `true`.
21+
/// - Parameter error: The error to throw if the value is `false`.
22+
/// - Returns: `true` if the value was `false` (discardable result).
23+
@discardableResult
24+
func requireFalse<E: Error>(_ error: E) throws(E) -> Self {
25+
guard !self else { throw error }
26+
return true
27+
}
28+
}
29+
30+
public extension Optional where Wrapped == Bool {
31+
/// Throws an error if the wrapped bool value is `false` or if the optional is `nil`.
32+
/// - Parameter error: The error to throw if the wrapped value is `false` or the optional is `nil`.
33+
/// - Returns: `true` if the wrapped value was `true` (discardable result).
34+
@discardableResult
35+
func requireTrue<E: Error>(_ error: E) throws(E) -> Bool {
36+
guard let self else { throw error }
37+
guard self else { throw error }
38+
return true
39+
}
40+
41+
/// Throws an error if the wrapped bool value is `true` or if the optional is `nil`.
42+
/// - Parameter error: The error to throw if the wrapped value is `true` or the optional is `nil`.
43+
/// - Returns: `true` if the wrapped value was `false` (discardable result).
44+
@discardableResult
45+
func requireFalse<E: Error>(_ error: E) throws(E) -> Bool {
46+
guard let self else { throw error }
47+
guard !self else { throw error }
48+
return true
49+
}
50+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
public extension Collection {
2+
/// Throws an error if the collection is empty.
3+
/// - Parameter error: The error to throw if the collection is empty.
4+
/// - Returns: The non-empty collection (discardable result).
5+
@discardableResult
6+
func requireNotEmpty<E: Error>(_ error: E) throws(E) -> Self {
7+
try isEmpty.requireFalse(error)
8+
return self
9+
}
10+
11+
/// Throws an error if the collection has one or more elements.
12+
/// - Parameter error: The error to throw if the collection has one or more elements.
13+
/// - Returns: The empty collection (discardable result).
14+
@discardableResult
15+
func requireEmpty<E: Error>(_ error: E) throws(E) -> Self {
16+
try isEmpty.requireTrue(error)
17+
return self
18+
}
19+
}
File renamed without changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import Foundation
2+
3+
/// Attempts to cast a value to a specific type, throwing an error if casting fails.
4+
/// - Parameters:
5+
/// - value: The value to be cast.
6+
/// - type: The type ot attempt to cast the value to.
7+
/// - error: The error to throw if casting the value fails.
8+
/// - Throws: The specified error if casting fails.
9+
/// - Returns: The value cast to the specified type.
10+
///
11+
/// This overload is provided for use where the type can not be inferred by the compiler, allowing the type to be specified manually.
12+
@_disfavoredOverload
13+
@discardableResult
14+
public func cast<T, E: Error>(_ value: @autoclosure () -> Any?, as type: T.Type, _ error: E) throws(E) -> T {
15+
try cast(value(), error: error)
16+
}
17+
18+
/// Attempts to cast a value to the expected type, throwing an error if casting fails.
19+
/// - Parameters:
20+
/// - value: The value to be cast.
21+
/// - error: The error to throw if casting the value fails.
22+
/// - Throws: The specified error if casting fails.
23+
/// - Returns: The value cast to the expected type.
24+
///
25+
/// This overload is provided for use where the type can be inferred by the compiler.
26+
@discardableResult
27+
public func cast<T, E: Error>(_ value: @autoclosure () -> Any?, error: E) throws(E) -> T {
28+
let rawValue = try value().require(error)
29+
guard let cast = rawValue as? T else { throw error }
30+
return cast
31+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import Foundation
2+
import Testing
3+
@testable import BuddyFoundation
4+
5+
private struct TestError: Error { }
6+
7+
@Suite
8+
struct RequireTests {
9+
@Test func testBoolRequireTrue() throws {
10+
#expect(throws: TestError.self) {
11+
try false.require(TestError())
12+
try false.requireTrue(TestError())
13+
}
14+
15+
#expect(try true.require(TestError()))
16+
#expect(try true.requireTrue(TestError()))
17+
}
18+
19+
@Test func testBoolRequireFalse() throws {
20+
#expect(throws: TestError.self) {
21+
try true.requireFalse(TestError())
22+
}
23+
24+
#expect(try false.requireFalse(TestError()))
25+
}
26+
27+
@Test func testOptionalBoolRequireTrue() throws {
28+
#expect(throws: TestError.self) {
29+
try Optional<Bool>.some(false).requireTrue(TestError())
30+
}
31+
32+
#expect(try Optional<Bool>.some(true).requireTrue(TestError()))
33+
}
34+
35+
@Test func testOptionalBoolRequireFalse() throws {
36+
#expect(throws: TestError.self) {
37+
try Optional<Bool>.some(true).requireFalse(TestError())
38+
}
39+
40+
#expect(try Optional<Bool>.some(false).requireFalse(TestError()))
41+
}
42+
43+
@Test func testOptionalRequire() throws {
44+
#expect(throws: TestError.self) {
45+
let string: String? = nil
46+
try string.require(TestError())
47+
}
48+
49+
let string: String? = "Hello"
50+
#expect(try string.require(TestError()) == "Hello")
51+
}
52+
53+
@Test func testCollectionRequireNotEmpty() throws {
54+
#expect(throws: TestError.self) {
55+
try Array<String>().requireNotEmpty(TestError())
56+
}
57+
58+
let array: [String] = ["Hello"]
59+
#expect(try array.requireNotEmpty(TestError()) == ["Hello"])
60+
}
61+
62+
@Test func testCollectionRequireEmpty() throws {
63+
#expect(throws: TestError.self) {
64+
let array: [String] = ["Hello"]
65+
try array.requireEmpty(TestError())
66+
}
67+
68+
#expect(try Array<String>().requireEmpty(TestError()) == [])
69+
}
70+
71+
@Test func testCastRequire() throws {
72+
let erasedValue: Any? = "Hello"
73+
74+
#expect(throws: TestError.self) {
75+
let _: Int = try cast(erasedValue, error: TestError())
76+
}
77+
78+
#expect(try cast(erasedValue, error: TestError()) == "Hello")
79+
}
80+
}

0 commit comments

Comments
 (0)