├── .gitignore ├── Package.swift ├── README.md ├── Sources └── Buildable │ ├── BuildSet.swift │ ├── Buildable.swift │ ├── FunctionBuilder.swift │ └── _Builder.swift └── Tests ├── BuildableTests ├── BuildableTests.swift └── XCTestManifests.swift └── LinuxMain.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | .swiftpm -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Buildable", 8 | platforms: [ 9 | .iOS(.v8), 10 | .macOS(.v10_10), 11 | .tvOS(.v9), 12 | .watchOS(.v2) 13 | ], 14 | products: [ 15 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 16 | .library( 17 | name: "Buildable", 18 | targets: ["Buildable"]), 19 | ], 20 | dependencies: [ 21 | // Dependencies declare other packages that this package depends on. 22 | // .package(url: /* package url */, from: "1.0.0"), 23 | ], 24 | targets: [ 25 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 26 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 27 | .target( 28 | name: "Buildable", 29 | dependencies: []), 30 | .testTarget( 31 | name: "BuildableTests", 32 | dependencies: ["Buildable"]), 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Buildable 2 | 3 | Add FunctionBuilder syntax using Buildable Protocol. 4 | 5 | ```swift 6 | extension Array: Buildable { 7 | static func empty() -> Array { [] } 8 | static func merge(_ lhs: Array, _ rhs: Array) -> Array { 9 | lhs + rhs 10 | } 11 | } 12 | 13 | [Int].build { 14 | [1, 2, 3] 15 | if myCondition { 16 | [4, 5, 6] 17 | } else { 18 | [7, 8, 9] 19 | } 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /Sources/Buildable/BuildSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BuildSet.swift 3 | // 4 | // 5 | // Created by tarunon on 2020/08/27. 6 | // 7 | 8 | /// Intermediate objects for function builder 9 | public enum BuildSet { 10 | public struct Empty{ 11 | } 12 | 13 | public struct Tuple2 { 14 | var c0: C0 15 | var c1: C1 16 | } 17 | 18 | public struct Tuple3 { 19 | var c0: C0 20 | var c1: C1 21 | var c2: C2 22 | } 23 | 24 | public struct Tuple4 { 25 | var c0: C0 26 | var c1: C1 27 | var c2: C2 28 | var c3: C3 29 | } 30 | 31 | public struct Tuple5 { 32 | var c0: C0 33 | var c1: C1 34 | var c2: C2 35 | var c3: C3 36 | var c4: C4 37 | } 38 | 39 | public struct Tuple6 { 40 | var c0: C0 41 | var c1: C1 42 | var c2: C2 43 | var c3: C3 44 | var c4: C4 45 | var c5: C5 46 | } 47 | 48 | public struct Tuple7 { 49 | var c0: C0 50 | var c1: C1 51 | var c2: C2 52 | var c3: C3 53 | var c4: C4 54 | var c5: C5 55 | var c6: C6 56 | } 57 | 58 | public struct Tuple8 { 59 | var c0: C0 60 | var c1: C1 61 | var c2: C2 62 | var c3: C3 63 | var c4: C4 64 | var c5: C5 65 | var c6: C6 66 | var c7: C7 67 | } 68 | 69 | public struct Tuple9 { 70 | var c0: C0 71 | var c1: C1 72 | var c2: C2 73 | var c3: C3 74 | var c4: C4 75 | var c5: C5 76 | var c6: C6 77 | var c7: C7 78 | var c8: C8 79 | } 80 | 81 | public enum Either { 82 | case c0(C0) 83 | case c1(C1) 84 | } 85 | } 86 | 87 | extension BuildSet.Empty: _Builder where C: _Builder { 88 | public func _build() -> C.BuildTarget { 89 | .empty() 90 | } 91 | } 92 | 93 | extension BuildSet.Tuple2: _Builder 94 | where C0: _Builder, C1: _Builder, C0.BuildTarget == C1.BuildTarget { 95 | public func _build() -> C0.BuildTarget { 96 | c0._build() 97 | .merging(c1._build()) 98 | } 99 | } 100 | 101 | extension BuildSet.Tuple3: _Builder 102 | where C0: _Builder, C1: _Builder, C2: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget { 103 | public func _build() -> C0.BuildTarget { 104 | c0._build() 105 | .merging(c1._build()) 106 | .merging(c2._build()) 107 | } 108 | } 109 | 110 | 111 | extension BuildSet.Tuple4: _Builder 112 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget { 113 | public func _build() -> C0.BuildTarget { 114 | c0._build() 115 | .merging(c1._build()) 116 | .merging(c2._build()) 117 | .merging(c3._build()) 118 | } 119 | } 120 | 121 | extension BuildSet.Tuple5: _Builder 122 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C4: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget, C0.BuildTarget == C4.BuildTarget { 123 | public func _build() -> C0.BuildTarget { 124 | c0._build() 125 | .merging(c1._build()) 126 | .merging(c2._build()) 127 | .merging(c3._build()) 128 | .merging(c4._build()) 129 | } 130 | } 131 | 132 | extension BuildSet.Tuple6: _Builder 133 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C4: _Builder, C5: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget, C0.BuildTarget == C4.BuildTarget, C0.BuildTarget == C5.BuildTarget { 134 | public func _build() -> C0.BuildTarget { 135 | c0._build() 136 | .merging(c1._build()) 137 | .merging(c2._build()) 138 | .merging(c3._build()) 139 | .merging(c4._build()) 140 | .merging(c5._build()) 141 | } 142 | } 143 | 144 | extension BuildSet.Tuple7: _Builder 145 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C4: _Builder, C5: _Builder, C6: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget, C0.BuildTarget == C4.BuildTarget, C0.BuildTarget == C5.BuildTarget, C0.BuildTarget == C6.BuildTarget { 146 | public func _build() -> C0.BuildTarget { 147 | c0._build() 148 | .merging(c1._build()) 149 | .merging(c2._build()) 150 | .merging(c3._build()) 151 | .merging(c4._build()) 152 | .merging(c5._build()) 153 | .merging(c6._build()) 154 | } 155 | } 156 | 157 | extension BuildSet.Tuple8: _Builder 158 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C4: _Builder, C5: _Builder, C6: _Builder, C7: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget, C0.BuildTarget == C4.BuildTarget, C0.BuildTarget == C5.BuildTarget, C0.BuildTarget == C6.BuildTarget, C0.BuildTarget == C7.BuildTarget { 159 | public func _build() -> C0.BuildTarget { 160 | c0._build() 161 | .merging(c1._build()) 162 | .merging(c2._build()) 163 | .merging(c3._build()) 164 | .merging(c4._build()) 165 | .merging(c5._build()) 166 | .merging(c6._build()) 167 | .merging(c7._build()) 168 | } 169 | } 170 | 171 | extension BuildSet.Tuple9: _Builder 172 | where C0: _Builder, C1: _Builder, C2: _Builder, C3: _Builder, C4: _Builder, C5: _Builder, C6: _Builder, C7: _Builder, C8: _Builder, C0.BuildTarget == C1.BuildTarget, C0.BuildTarget == C2.BuildTarget, C0.BuildTarget == C3.BuildTarget, C0.BuildTarget == C4.BuildTarget, C0.BuildTarget == C5.BuildTarget, C0.BuildTarget == C6.BuildTarget, C0.BuildTarget == C7.BuildTarget, C0.BuildTarget == C8.BuildTarget { 173 | public func _build() -> C0.BuildTarget { 174 | c0._build() 175 | .merging(c1._build()) 176 | .merging(c2._build()) 177 | .merging(c3._build()) 178 | .merging(c4._build()) 179 | .merging(c5._build()) 180 | .merging(c6._build()) 181 | .merging(c7._build()) 182 | .merging(c8._build()) 183 | } 184 | } 185 | 186 | extension BuildSet.Either: _Builder 187 | where C0: _Builder, C1: _Builder, C0.BuildTarget == C1.BuildTarget { 188 | public func _build() -> C0.BuildTarget { 189 | switch self { 190 | case .c0(let c0): return c0._build() 191 | case .c1(let c1): return c1._build() 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /Sources/Buildable/Buildable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Buildable.swift 3 | // App 4 | // 5 | // Created by tarunon on 2020/08/27. 6 | // 7 | 8 | /// Adding FucntionBuilder feature in some Type. 9 | public protocol Buildable: _Builder { 10 | static func empty() -> Self 11 | func merging(_ other: Self) -> Self 12 | } 13 | 14 | extension Buildable { 15 | public func _build() -> Self { self } 16 | 17 | public static func build(@FunctionBuilder _ builder: () -> C) -> Self 18 | where C: _Builder, C.BuildTarget == Self { 19 | return builder()._build() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Buildable/FunctionBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FunctionBuilder.swift 3 | // 4 | // 5 | // Created by tarunon on 2020/08/27. 6 | // 7 | 8 | /// Generic function builder 9 | @_functionBuilder 10 | public struct FunctionBuilder { 11 | public typealias Empty = BuildSet.Empty 12 | public typealias Tuple2 = BuildSet.Tuple2 13 | public typealias Tuple3 = BuildSet.Tuple3 14 | public typealias Tuple4 = BuildSet.Tuple4 15 | public typealias Tuple5 = BuildSet.Tuple5 16 | public typealias Tuple6 = BuildSet.Tuple6 17 | public typealias Tuple7 = BuildSet.Tuple7 18 | public typealias Tuple8 = BuildSet.Tuple8 19 | public typealias Tuple9 = BuildSet.Tuple9 20 | public typealias Either = BuildSet.Either 21 | 22 | public static func buildBlock() -> Empty { 23 | .init() 24 | } 25 | 26 | public static func buildBlock(_ c: C) -> C { 27 | c 28 | } 29 | 30 | public static func buildBlock(_ c0: C0, _ c1: C1) -> Tuple2 { 31 | .init( 32 | c0: c0, 33 | c1: c1 34 | ) 35 | } 36 | 37 | public static func buildIf(_ c: C?) -> Either> { 38 | if let c = c { 39 | return .c0(c) 40 | } else { 41 | return .c1(.init()) 42 | } 43 | } 44 | 45 | public static func buildEither(first: T) -> Either { 46 | .c0(first) 47 | } 48 | 49 | public static func buildEither(second: F) -> Either { 50 | .c1(second) 51 | } 52 | } 53 | 54 | extension FunctionBuilder { 55 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2) -> Tuple3< 56 | C0, C1, C2 57 | > { 58 | .init( 59 | c0: c0, 60 | c1: c1, 61 | c2: c2 62 | ) 63 | } 64 | 65 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3) -> Tuple4< 66 | C0, C1, C2, C3 67 | > { 68 | .init( 69 | c0: c0, 70 | c1: c1, 71 | c2: c2, 72 | c3: c3 73 | ) 74 | } 75 | 76 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4) -> Tuple5< 77 | C0, C1, C2, C3, C4 78 | > { 79 | .init( 80 | c0: c0, 81 | c1: c1, 82 | c2: c2, 83 | c3: c3, 84 | c4: c4 85 | ) 86 | } 87 | 88 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5) -> Tuple6< 89 | C0, C1, C2, C3, C4, C5 90 | > { 91 | .init( 92 | c0: c0, 93 | c1: c1, 94 | c2: c2, 95 | c3: c3, 96 | c4: c4, 97 | c5: c5 98 | ) 99 | } 100 | 101 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6) -> Tuple7< 102 | C0, C1, C2, C3, C4, C5, C6 103 | > { 104 | .init( 105 | c0: c0, 106 | c1: c1, 107 | c2: c2, 108 | c3: c3, 109 | c4: c4, 110 | c5: c5, 111 | c6: c6 112 | ) 113 | } 114 | 115 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7) -> Tuple8< 116 | C0, C1, C2, C3, C4, C5, C6, C7 117 | > { 118 | .init( 119 | c0: c0, 120 | c1: c1, 121 | c2: c2, 122 | c3: c3, 123 | c4: c4, 124 | c5: c5, 125 | c6: c6, 126 | c7: c7 127 | ) 128 | } 129 | 130 | public static func buildBlock(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6, _ c7: C7, _ c8: C8) -> Tuple9< 131 | C0, C1, C2, C3, C4, C5, C6, C7, C8 132 | > { 133 | .init( 134 | c0: c0, 135 | c1: c1, 136 | c2: c2, 137 | c3: c3, 138 | c4: c4, 139 | c5: c5, 140 | c6: c6, 141 | c7: c7, 142 | c8: c8 143 | ) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Sources/Buildable/_Builder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _Builder.swift 3 | // 4 | // 5 | // Created by tarunon on 2020/08/27. 6 | // 7 | 8 | public protocol _Builder { 9 | associatedtype BuildTarget: Buildable 10 | func _build() -> BuildTarget 11 | } 12 | -------------------------------------------------------------------------------- /Tests/BuildableTests/BuildableTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Buildable 3 | 4 | extension Array: Buildable { 5 | public static func empty() -> Array { 6 | [] 7 | } 8 | 9 | public func merging(_ other: Array) -> Array { 10 | self + other 11 | } 12 | } 13 | 14 | final class BuildableTests: XCTestCase { 15 | func testFunctionBuilderWork() { 16 | let condition = true 17 | XCTAssertEqual( 18 | [Int].build { 19 | [1, 2, 3] 20 | if condition { 21 | [4, 5] 22 | } 23 | }, 24 | [1, 2, 3, 4, 5] 25 | ) 26 | XCTAssertEqual( 27 | [Int].build { 28 | [1, 2, 3] 29 | if !condition { 30 | [0] 31 | } else { 32 | [4, 5] 33 | } 34 | }, 35 | [1, 2, 3, 4, 5] 36 | ) 37 | } 38 | 39 | static var allTests = [ 40 | ("testFunctionBuilderWork", testFunctionBuilderWork), 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /Tests/BuildableTests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(BuildableTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import BuildableTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += BuildableTests.allTests() 7 | XCTMain(tests) 8 | --------------------------------------------------------------------------------