Last active
September 7, 2023 07:20
-
-
Save rjchatfield/72629b22fa915f72bfddd96a96c541eb to your computer and use it in GitHub Desktop.
ArrayBuilder - Swift ~~FunctionBuilder~~ ResultBuilder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@resultBuilder | |
public struct ArrayBuilder<Element> { | |
public static func buildPartialBlock(first: Element) -> [Element] { [first] } | |
public static func buildPartialBlock(first: [Element]) -> [Element] { first } | |
public static func buildPartialBlock(accumulated: [Element], next: Element) -> [Element] { accumulated + [next] } | |
public static func buildPartialBlock(accumulated: [Element], next: [Element]) -> [Element] { accumulated + next } | |
// Empty Case | |
public static func buildBlock() -> [Element] { [] } | |
// If/Else | |
public static func buildEither(first: [Element]) -> [Element] { first } | |
public static func buildEither(second: [Element]) -> [Element] { second } | |
// Just ifs | |
public static func buildIf(_ element: [Element]?) -> [Element] { element ?? [] } | |
// fatalError() | |
public static func buildPartialBlock(first: Never) -> [Element] {} | |
} | |
// MARK: - Array.init(builder:) | |
public extension Array { | |
init(@ArrayBuilder<Element> builder: () -> [Element]) { | |
self.init(builder()) | |
} | |
} | |
// MARK: - | |
//@resultBuilder | |
//public struct ArrayBuilder_OLD<T> { | |
// // Empty Case | |
// public static func buildBlock() -> [T] { [] } | |
// // Single case | |
// public static func buildBlock(_ expression: T) -> [T] { [expression] } | |
// // Multple items | |
// public static func buildBlock(_ elements: T...) -> [T] { elements } | |
// // Multiple function builders | |
// public static func buildBlock(_ elementss: [T]...) -> [T] { elementss.flatMap({ $0 }) } | |
// // If/Else | |
// public static func buildBlock(_ elements: [T]) -> [T] { elements } | |
// public static func buildEither(first: [T]) -> [T] { first } | |
// public static func buildEither(second: [T]) -> [T] { second } | |
// // Just ifs | |
// public static func buildIf(_ element: [T]?) -> [T] { element ?? [] } | |
// // fatalError() | |
// public static func buildBlock(_ element: Never) -> [T] {} | |
// | |
////// static func buildBlock(_ element: T, _ elements: T...) -> [T] { [element] + elements } | |
//// // 1x Single + 1x Array | |
////// static func buildBlock(_ e1: T, _ elements: [T]) -> [T] { [e1] + elements } | |
//// static func buildBlock(_ e1: T, _ elements1: [T], _ elements2: T...) -> [T] { [e1] + elements1 + elements2 } | |
////// static func buildBlock(_ elements: [T], _ e1: T) -> [T] { [e1] + elements } | |
//// | |
//// // 2x Singles + 1x Array | |
//// static func buildBlock(_ e1: T, _ e2: T, _ elements: [T]) -> [T] { [e1, e2] + elements } | |
////// static func buildBlock(_ es: T..., _ elements: [T]) -> [T] { es... + elements } // A parameter following a variadic parameter requires a label | |
////// static func buildBlock(_ e1: T, _ elements: [T], _ e2: T) -> [T] { [e1] + elements + [e2] } | |
////// static func buildBlock(_ elements: [T], _ e1: T, _ e2: T) -> [T] { elements + [e1, e2] } | |
//// static func buildBlock(_ elements1: [T], _ elements2: T...) -> [T] { elements1 + elements2 } | |
//// | |
//// // 3x Singles + 1x Array | |
//// static func buildBlock(_ e1: T, _ e2: T, _ e3: T, _ elements: [T]) -> [T] { [e1, e2, e3] + elements } | |
//// static func buildBlock(_ e1: T, _ e2: T, _ elements: [T], _ e3: T) -> [T] { [e1, e2] + elements + [e3] } | |
//// static func buildBlock(_ e1: T, _ elements: [T], _ e2: T, _ e3: T) -> [T] { [e1] + elements + [e2, e3] } | |
//// static func buildBlock(_ elements: [T], _ e1: T, _ e2: T, _ e3: T) -> [T] { elements + [e1, e2, e3] } | |
//// | |
//// // 1x Single + 2x Arrays | |
//// static func buildBlock(_ elements1: [T], _ e1: T, _ elements2: [T]) -> [T] { elements1 + [e1] + elements2 } | |
//// | |
//// // 2x Singles + 2x Arrays | |
//// static func buildBlock(_ elements1: [T], _ e1: T, _ elements2: [T], _ e2: T) -> [T] { elements1 + [e1] + elements2 + [e2] } | |
//// static func buildBlock(_ elements1: [T], _ e1: T, _ e2: T, _ elements2: [T]) -> [T] { elements1 + [e1, e2] + elements2 } | |
//// static func buildBlock(_ e1: T, _ elements1: [T], _ e2: T, _ elements2: [T]) -> [T] { [e1] + elements1 + [e2] + elements2 } | |
//// | |
//// // 2x Singles + 3x Arrays | |
//// static func buildBlock(_ elements1: [T], _ e1: T, _ elements2: [T], _ e2: T, _ elements3: [T]) -> [T] { elements1 + [e1] + elements2 + [e2] + elements2 } | |
// | |
// // Mix Arrays and Singles. | |
// // But this is a work around for "A parameter following a variadic parameter requires a label" | |
// | |
// // 1x Arrays (up to 3x singles) | |
// public static func buildBlock(_ array1: [T], _ trailing: T...) -> [T] { array1 + trailing } | |
// public static func buildBlock(_ e1: T, _ array1: [T], _ trailing: T...) -> [T] { [e1] + array1 + trailing } | |
// public static func buildBlock(_ e1: T, _ e2: T, _ array1: [T], _ trailing: T...) -> [T] { [e1, e2] + array1 + trailing } | |
// public static func buildBlock(_ e1: T, _ e2: T, _ e3: T, _ array1: [T], _ trailing: T...) -> [T] { [e1, e2, e3] + array1 + trailing } | |
// | |
// // 2x Arrays (up to 3x singles) | |
// public static func buildBlock(_ array1: [T], _ array2: [T], _ e1: T, _ trailing: T...) -> [T] { array1 + array2 + [e1] + trailing } // to avoid ambiguity ([T]...) | |
// public static func buildBlock(_ array1: [T], _ e1: T, _ array2: [T], _ trailing: T...) -> [T] { array1 + [e1] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ array1: [T], _ array2: [T], _ trailing: T...) -> [T] { [e1] + array1 + array2 + trailing } | |
// | |
// public static func buildBlock(_ array1: [T], _ e1: T, _ e2: T, _ array2: [T], _ trailing: T...) -> [T] { array1 + [e1, e2] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ array1: [T], _ e2: T, _ array2: [T], _ trailing: T...) -> [T] { [e1] + array1 + [e2] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ e2: T, _ array1: [T], _ array2: [T], _ trailing: T...) -> [T] { [e1, e2] + array1 + array2 + trailing } | |
// | |
// public static func buildBlock(_ array1: [T], _ e1: T, _ e2: T, _ e3: T, _ array2: [T], _ trailing: T...) -> [T] { array1 + [e1, e2, e3] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ array1: [T], _ e2: T, _ e3: T, _ array2: [T], _ trailing: T...) -> [T] { [e1] + array1 + [e2, e3] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ e2: T, _ array1: [T], _ e3: T, _ array2: [T], _ trailing: T...) -> [T] { [e1, e2] + array1 + [e3] + array2 + trailing } | |
// public static func buildBlock(_ e1: T, _ e2: T, _ e3: T, _ array1: [T], _ array2: [T], _ trailing: T...) -> [T] { [e1, e2, e3] + array1 + array2 + trailing } | |
// | |
//// static func buildBlock(_ expression: T?) -> [T] { expression.map({ [$0] }) ?? [] } | |
//// static func buildBlock(_ elements: [T]?) -> [T] { elements ?? [] } | |
//// static func buildBlock(_ elements: T?...) -> [T] { elements.compactMap({ $0 }) } | |
//// static func buildBlock(_ elementss: [[T]?]) -> [T] { elementss.flatMap({ $0 ?? [] }) } | |
//// static func buildBlock(_ elementss: [T]?...) -> [T] { elementss.flatMap({ $0 ?? [] }) } | |
//// static func buildBlock(_ elementss: [T?]...) -> [T] { elementss.flatMap({ $0.compactMap({ $0 }) }) } | |
// | |
//// static func buildIf(_ element: T?) -> [T] { element.map { [$0] } ?? [] } | |
//// static func buildIf(_ elements: [T]?) -> [T] { elements ?? [] } | |
//// static func buildEither(first: T) -> [T] { [first] } | |
//// static func buildEither(second: T) -> [T] { [second] } | |
//// static func buildEither(first: T...) -> [T] { first } | |
//// static func buildEither(second: T...) -> [T] { second } | |
// | |
//// static func buildBlock(_ either: Either<T>...) -> [T] { either.flatMap { $0.asArray } } | |
//// | |
//// enum Either<T> { | |
//// case single(T) | |
//// case array([T]) | |
//// | |
//// var asArray: [T] { | |
//// switch self { | |
//// case .single(let el): return [el] | |
//// case .array(let els): return els | |
//// } | |
//// } | |
//// } | |
//} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XCTest | |
final class ArrayBuilderTests: XCTestCase { | |
let bool = false | |
func testExample() throws { | |
expect(["hello"]) { | |
"hello" | |
} | |
expect(["hello", "world"]) { | |
"hello" | |
"world" | |
} | |
expect(["world"]) { | |
if bool { | |
"hello" | |
} else { | |
"world" | |
} | |
} | |
expect(["world", "world"]) { | |
if bool { | |
"hello" | |
"hello" | |
} else { | |
"world" | |
"world" | |
} | |
} | |
expect(["world", "world"]) { | |
if bool { | |
"hello" | |
"hello" | |
} else if !bool { | |
"world" | |
"world" | |
} else { | |
"" | |
} | |
} | |
expect([]) { | |
if bool { | |
"hello" | |
} | |
} | |
expect(["hello"]) { | |
if !bool { | |
if !bool { | |
"hello" | |
} | |
} | |
} | |
expect(["world"]) { | |
if bool { | |
"hello" | |
} | |
if !bool { | |
"world" | |
} | |
} | |
expect(["foo", "world"]) { // [String] or [[String]] ?? | |
"foo" // :String | |
if bool { | |
"hello" // :String | |
} else { | |
"world" // :String | |
} // :[String] | |
} | |
expect(["foo", "world"]) { | |
["foo"] | |
if bool { | |
"hello" | |
} else { | |
"world" | |
} | |
} | |
expect(["world1", "world2"]) { | |
if bool { | |
"hello1" | |
} else { | |
"world1" | |
} | |
if bool { | |
"hello2" | |
} else { | |
"world2" | |
} | |
} | |
//expect(["hello"]) { | |
// guard bool else { | |
// return [] | |
// } | |
// "bar" | |
// "baz" | |
//} | |
expect([]) { | |
} | |
func fatal() { | |
expect(["hello"]) { | |
fatalError() | |
} | |
expect(["hello"]) { | |
if bool { | |
fatalError() | |
} | |
} | |
expect(["hello"]) { | |
if bool { | |
fatalError() | |
} else { | |
fatalError() | |
} | |
} | |
// This works, just has a warning | |
// _ = expect(["hello"]) { | |
// if bool { | |
// "hello" | |
// fatalError() | |
// } else { | |
// "world" | |
// fatalError() | |
// } | |
// } | |
} | |
//expect(["hello"]) { | |
// for str in ["hello", "world"] { | |
// str | |
// } | |
//} | |
expect(["hello", "world"]) { | |
["hello", "world"].map { str in | |
str | |
} | |
} | |
expect(["world"]) { | |
switch bool { | |
case true: "hello" | |
case false: "world" | |
} | |
} | |
} | |
func test2() { | |
XCTAssertEqual(["hello"], Array<String>(builder: { | |
"hello" | |
})) | |
XCTAssertEqual(["hello", "world"], Array<String>(builder: { | |
"hello" | |
"world" | |
})) | |
XCTAssertEqual(["world"], Array<String>(builder: { | |
if bool { | |
"hello" | |
} else { | |
"world" | |
} | |
})) | |
XCTAssertEqual([], Array<String>(builder: { | |
if bool { | |
"hello" | |
} | |
})) | |
XCTAssertEqual(["world", "world"], Array<String>(builder: { | |
"world" | |
"world" | |
if bool { | |
"hello" | |
"optional" | |
} | |
})) | |
XCTAssertEqual(["world", "world"], Array<String>(builder: { | |
"world" | |
if bool { | |
"hello" | |
"optional" | |
} | |
"world" | |
})) | |
XCTAssertEqual(["world", "world"], Array<String>(builder: { | |
if bool { | |
"hello" | |
"optional" | |
} | |
"world" | |
"world" | |
})) | |
XCTAssertEqual(["world", "world"], Array<String>(builder: { | |
if bool { | |
"hello" | |
"optional" | |
} | |
if bool { | |
"hello" | |
"optional" | |
} | |
"world" | |
"world" | |
})) | |
XCTAssertEqual(["world"], Array<String>(builder: { | |
["world"] | |
if bool { | |
"hello" | |
"optional" | |
} | |
})) | |
} | |
func expect(_ expected: [String], @ArrayBuilder<String> block: () -> [String]) { | |
XCTAssertEqual(expected, block()) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment